ProcessWire - Blog: Unterschied zwischen den Versionen

Aus Wikizone
Wechseln zu: Navigation, Suche
(Die Seite wurde neu angelegt: „Wie erstellt man einen Blog oder News-Section in ProcessWire ? == Strategien == === Strategie I - News mit einfachem Page Tree === Einfacher Aufbau mit Index…“)
 
 
(16 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
Wie erstellt man einen Blog oder News-Section in ProcessWire ?
+
Wie erstellt man einen Blog oder eine News-Section in ProcessWire ?
  
== Strategien ==
+
== ProcessBlog Modul von Kongondo ==
 +
Zu viel Zeug drin ;-)
 +
https://processwire.com/modules/process-blog/
 +
ProcessBlog
  
=== Strategie I - News mit einfachem Page Tree ===
+
== Eigenes Blog Modul erstellen ==
Einfacher Aufbau mit Indexseite (Listseite) Und Unterseiten mit jeweils einer News
+
Todo
* Alle News sind Seiten
 
* Die Newsseiten haben das Template "news-single"
 
* Die News liegen in einer Elternseite vom Template-Typ "news-list"
 
* Die Elternseite ist auch die Listenansicht der News.
 
  
==== Aufbau ====
+
== Beispiel mit PageReference für Kategorien ==
* '''Template news-list.php''' anlegen und im Tab URLs Allow Page Numbers anhaken.
 
* ''' Template news.php''' anlegen
 
* Felder für news.php anlegen und zuordnen. Z.B. news_headline, news_date, news_teaser, body
 
  
=== Strategie II - Kategorien ===
+
=== Paging ===
 +
=== Module ===
 +
* Comments / Kommentare (Core)
 +
* InputfieldCommentsAdmin
 +
* FieldtypeComments
 +
https://processwire.com/docs/modules/guides/comments/
 +
 
 +
=== Felder ===
 +
* date (Datum des Blog-Eintrags)
 +
* comments (Verwaltet Kommentare für eine Seite. Hier für einen Blog-Eintrag)
 +
* tags (Legt die Tags/Kategorien für einen Blogeintrag fest) - '''Hier mußt du noch die Parent Page festlegen'''
 +
* pr_single (Legt die Seite für die Einzelansicht in der Blog-Seite fest)
 +
 
 +
=== Templates ===
 +
* tag (steht für eine Blog-Kategorie)
 +
* tags (unterhalb dieser Seite kann man nur Kategorien / Tags anlegen)
 +
* blog (Gibt den Blog aus)
 +
* blog-post (Steht für einen einzelnen Blogeintrag und für die Einzelansicht)
 +
 
 +
=== Rechte ===
 +
Benutzerrolle: editor
 +
* category.add
 +
* category.edit
 +
* category.create
 +
 
 +
=== Kopiervorlage ===
 +
==== Felder ====
 +
<pre>
 +
{
 +
    "comments": {
 +
        "id": 134,
 +
        "name": "comments",
 +
        "label": "Comments",
 +
        "flags": 0,
 +
        "type": "FieldtypeComments",
 +
        "schemaVersion": 6,
 +
        "moderate": 1,
 +
        "redirectAfterPost": 1,
 +
        "quietSave": 1,
 +
        "deleteSpamDays": 3,
 +
        "useWebsite": 1,
 +
        "dateFormat": "relative",
 +
        "useGravatar": "g",
 +
        "tags": "blog",
 +
        "icon": "comment-o",
 +
        "notificationEmail": "",
 +
        "fromEmail": "",
 +
        "notifySpam": "",
 +
        "useNotify": 0,
 +
        "useAkismet": "",
 +
        "depth": 0,
 +
        "sortNewest": "",
 +
        "useVotes": 0,
 +
        "useStars": 0,
 +
        "collapsed": 0,
 +
        "showIf": "",
 +
        "themeOffset": "",
 +
        "themeBorder": "",
 +
        "themeColor": "",
 +
        "columnWidth": 100,
 +
        "required": "",
 +
        "requiredIf": ""
 +
    },
 +
    "date": {
 +
        "id": 133,
 +
        "name": "date",
 +
        "label": "Date",
 +
        "flags": 0,
 +
        "type": "FieldtypeDatetime",
 +
        "dateOutputFormat": "j.n.Y",
 +
        "size": 25,
 +
        "datepicker": 3,
 +
        "dateInputFormat": "d.m.Y",
 +
        "defaultToday": 1,
 +
        "placeholder": "yyyy/mm/dd",
 +
        "icon": "calendar",
 +
        "tags": "blog",
 +
        "inputType": "text",
 +
        "dateSelectFormat": "yMd",
 +
        "yearFrom": 1921,
 +
        "yearTo": 2041,
 +
        "htmlType": "date",
 +
        "collapsed": 0,
 +
        "timeInputSelect": 0,
 +
        "yearLock": 0,
 +
        "columnWidth": 20,
 +
        "showIf": "",
 +
        "themeOffset": "",
 +
        "themeBorder": "",
 +
        "themeColor": "",
 +
        "required": "",
 +
        "requiredAttr": "",
 +
        "requiredIf": "",
 +
        "dateMin": "",
 +
        "dateMax": "",
 +
        "timeStep": "",
 +
        "timeMin": "",
 +
        "timeMax": "",
 +
        "timeInputFormat": "",
 +
        "yearRange": ""
 +
    }
 +
}
 +
</pre>
 +
 
 +
==== Templates ====
 +
<pre>
 +
{
 +
    "blog": {
 +
        "id": 76,
 +
        "name": "blog",
 +
        "fieldgroups_id": "blog",
 +
        "flags": 0,
 +
        "cache_time": 0,
 +
        "useRoles": 1,
 +
        "editRoles": [
 +
            "editor"
 +
        ],
 +
        "addRoles": [
 +
            "editor"
 +
        ],
 +
        "createRoles": [
 +
            "editor"
 +
        ],
 +
        "rolesPermissions": [],
 +
        "noInherit": 0,
 +
        "childrenTemplatesID": 0,
 +
        "sortfield": "",
 +
        "noChildren": "",
 +
        "noParents": "",
 +
        "childTemplates": [],
 +
        "parentTemplates": [],
 +
        "allowPageNum": 1,
 +
        "allowChangeUser": 0,
 +
        "redirectLogin": 0,
 +
        "urlSegments": 1,
 +
        "https": 0,
 +
        "slashUrls": 1,
 +
        "slashPageNum": 0,
 +
        "slashUrlSegments": 0,
 +
        "altFilename": "",
 +
        "guestSearchable": 0,
 +
        "pageClass": "",
 +
        "childNameFormat": "",
 +
        "pageLabelField": "",
 +
        "noGlobal": 0,
 +
        "noMove": 0,
 +
        "noTrash": 0,
 +
        "noSettings": 0,
 +
        "noChangeTemplate": 0,
 +
        "noShortcut": 0,
 +
        "noUnpublish": 0,
 +
        "noLang": 0,
 +
        "compile": 3,
 +
        "nameContentTab": 0,
 +
        "noCacheGetVars": "",
 +
        "noCachePostVars": "",
 +
        "useCacheForUsers": 0,
 +
        "cacheExpire": 0,
 +
        "cacheExpirePages": [],
 +
        "cacheExpireSelector": "",
 +
        "label": "Blog",
 +
        "tags": "",
 +
        "titleNames": 0,
 +
        "noPrependTemplateFile": 0,
 +
        "noAppendTemplateFile": 0,
 +
        "prependFile": "",
 +
        "appendFile": "",
 +
        "pagefileSecure": false,
 +
        "tabContent": "",
 +
        "tabChildren": "",
 +
        "nameLabel": "",
 +
        "contentType": "",
 +
        "errorAction": 0,
 +
        "connectedFieldID": null,
 +
        "ns": "ProcessWire",
 +
        "_exportMode": true,
 +
        "roles": [
 +
            "guest",
 +
            "editor"
 +
        ],
 +
        "fieldgroupFields": [
 +
            "title",
 +
            "menus",
 +
            "pr_single"
 +
        ],
 +
        "fieldgroupContexts": {
 +
            "title": [],
 +
            "menus": [],
 +
            "pr_single": {
 +
                "columnWidth": 50,
 +
                "label": "Kategorien",
 +
                "notes": "Wähle die Seite aus, welche die Kategorien enthält."
 +
            }
 +
        }
 +
    },
 +
    "blog-post": {
 +
        "id": 74,
 +
        "name": "blog-post",
 +
        "fieldgroups_id": "blog-post",
 +
        "flags": 0,
 +
        "cache_time": -604800,
 +
        "useRoles": 0,
 +
        "noInherit": 1,
 +
        "childrenTemplatesID": 0,
 +
        "sortfield": "",
 +
        "noChildren": "",
 +
        "noParents": "",
 +
        "childTemplates": [],
 +
        "parentTemplates": [],
 +
        "allowPageNum": 0,
 +
        "allowChangeUser": 0,
 +
        "redirectLogin": 0,
 +
        "urlSegments": 0,
 +
        "https": 0,
 +
        "slashUrls": 1,
 +
        "slashPageNum": 0,
 +
        "slashUrlSegments": 0,
 +
        "altFilename": "",
 +
        "guestSearchable": 0,
 +
        "pageClass": "",
 +
        "childNameFormat": "",
 +
        "pageLabelField": "",
 +
        "noGlobal": 0,
 +
        "noMove": 0,
 +
        "noTrash": 0,
 +
        "noSettings": 0,
 +
        "noChangeTemplate": 0,
 +
        "noShortcut": 0,
 +
        "noUnpublish": 0,
 +
        "noLang": 0,
 +
        "compile": 3,
 +
        "nameContentTab": 0,
 +
        "noCacheGetVars": "",
 +
        "noCachePostVars": "",
 +
        "useCacheForUsers": 0,
 +
        "cacheExpire": 0,
 +
        "cacheExpirePages": [],
 +
        "cacheExpireSelector": "",
 +
        "label": "Blog-Post (News)",
 +
        "tags": "",
 +
        "titleNames": 0,
 +
        "noPrependTemplateFile": 0,
 +
        "noAppendTemplateFile": 0,
 +
        "prependFile": "",
 +
        "appendFile": "",
 +
        "pagefileSecure": false,
 +
        "tabContent": "",
 +
        "tabChildren": "",
 +
        "nameLabel": "",
 +
        "contentType": "",
 +
        "errorAction": 0,
 +
        "connectedFieldID": null,
 +
        "ns": "ProcessWire",
 +
        "_exportMode": true,
 +
        "fieldgroupFields": [
 +
            "title",
 +
            "date",
 +
            "body",
 +
            "images",
 +
            "files",
 +
            "categories",
 +
            "comments"
 +
        ],
 +
        "fieldgroupContexts": {
 +
            "title": [],
 +
            "date": {
 +
                "columnWidth": 25
 +
            },
 +
            "body": [],
 +
            "images": [],
 +
            "files": [],
 +
            "categories": [],
 +
            "comments": []
 +
        }
 +
    },
 +
    "categories": {
 +
        "id": 77,
 +
        "name": "categories",
 +
        "fieldgroups_id": "categories",
 +
        "flags": 0,
 +
        "cache_time": 0,
 +
        "useRoles": 1,
 +
        "editRoles": [
 +
            "editor"
 +
        ],
 +
        "addRoles": [
 +
            "editor"
 +
        ],
 +
        "createRoles": [
 +
            "editor"
 +
        ],
 +
        "rolesPermissions": [],
 +
        "noInherit": 0,
 +
        "childrenTemplatesID": 0,
 +
        "sortfield": "",
 +
        "noChildren": "",
 +
        "noParents": "",
 +
        "childTemplates": [
 +
            "category"
 +
        ],
 +
        "parentTemplates": [],
 +
        "allowPageNum": 0,
 +
        "allowChangeUser": 0,
 +
        "redirectLogin": 0,
 +
        "urlSegments": 0,
 +
        "https": 0,
 +
        "slashUrls": 1,
 +
        "slashPageNum": 0,
 +
        "slashUrlSegments": 0,
 +
        "altFilename": "",
 +
        "guestSearchable": 0,
 +
        "pageClass": "",
 +
        "childNameFormat": "",
 +
        "pageLabelField": "",
 +
        "noGlobal": 0,
 +
        "noMove": 0,
 +
        "noTrash": 0,
 +
        "noSettings": 0,
 +
        "noChangeTemplate": 0,
 +
        "noShortcut": 0,
 +
        "noUnpublish": 0,
 +
        "noLang": 0,
 +
        "compile": 3,
 +
        "nameContentTab": 0,
 +
        "noCacheGetVars": "",
 +
        "noCachePostVars": "",
 +
        "useCacheForUsers": 0,
 +
        "cacheExpire": 0,
 +
        "cacheExpirePages": [],
 +
        "cacheExpireSelector": "",
 +
        "label": "Kategorien",
 +
        "tags": "",
 +
        "titleNames": 0,
 +
        "noPrependTemplateFile": 0,
 +
        "noAppendTemplateFile": 0,
 +
        "prependFile": "",
 +
        "appendFile": "",
 +
        "pagefileSecure": false,
 +
        "tabContent": "",
 +
        "tabChildren": "",
 +
        "nameLabel": "",
 +
        "contentType": "",
 +
        "errorAction": 0,
 +
        "connectedFieldID": null,
 +
        "ns": "ProcessWire",
 +
        "_exportMode": true,
 +
        "roles": [
 +
            "guest",
 +
            "editor"
 +
        ],
 +
        "fieldgroupFields": [
 +
            "title"
 +
        ],
 +
        "fieldgroupContexts": {
 +
            "title": []
 +
        }
 +
    },
 +
    "category": {
 +
        "id": 75,
 +
        "name": "category",
 +
        "fieldgroups_id": "category",
 +
        "flags": 0,
 +
        "cache_time": 0,
 +
        "useRoles": 1,
 +
        "editRoles": [
 +
            "editor"
 +
        ],
 +
        "addRoles": [
 +
            "editor"
 +
        ],
 +
        "createRoles": [
 +
            "editor"
 +
        ],
 +
        "rolesPermissions": [],
 +
        "noInherit": 0,
 +
        "childrenTemplatesID": 0,
 +
        "sortfield": "",
 +
        "noChildren": "",
 +
        "noParents": "",
 +
        "childTemplates": [],
 +
        "parentTemplates": [
 +
            "categories"
 +
        ],
 +
        "allowPageNum": 0,
 +
        "allowChangeUser": 0,
 +
        "redirectLogin": 0,
 +
        "urlSegments": 0,
 +
        "https": 0,
 +
        "slashUrls": 1,
 +
        "slashPageNum": 0,
 +
        "slashUrlSegments": 0,
 +
        "altFilename": "",
 +
        "guestSearchable": 0,
 +
        "pageClass": "",
 +
        "childNameFormat": "",
 +
        "pageLabelField": "",
 +
        "noGlobal": 0,
 +
        "noMove": 0,
 +
        "noTrash": 0,
 +
        "noSettings": 0,
 +
        "noChangeTemplate": 0,
 +
        "noShortcut": 0,
 +
        "noUnpublish": 0,
 +
        "noLang": 0,
 +
        "compile": 3,
 +
        "nameContentTab": 0,
 +
        "noCacheGetVars": "",
 +
        "noCachePostVars": "",
 +
        "useCacheForUsers": 0,
 +
        "cacheExpire": 0,
 +
        "cacheExpirePages": [],
 +
        "cacheExpireSelector": "",
 +
        "label": "",
 +
        "tags": "",
 +
        "titleNames": 0,
 +
        "noPrependTemplateFile": 0,
 +
        "noAppendTemplateFile": 0,
 +
        "prependFile": "",
 +
        "appendFile": "",
 +
        "pagefileSecure": false,
 +
        "tabContent": "",
 +
        "tabChildren": "",
 +
        "nameLabel": "",
 +
        "contentType": "",
 +
        "errorAction": 0,
 +
        "connectedFieldID": null,
 +
        "ns": "ProcessWire",
 +
        "_exportMode": true,
 +
        "roles": [
 +
            "guest",
 +
            "editor"
 +
        ],
 +
        "fieldgroupFields": [
 +
            "title"
 +
        ],
 +
        "fieldgroupContexts": {
 +
            "title": []
 +
        }
 +
    }
 +
}
 +
</pre>
 +
 
 +
== Beispiel (Anlehnung an Ryans Blog Beispiel) ==
 +
=== Templates ===
 +
blog.php
 +
<syntaxhighlight lang="php">
 +
<?php namespace ProcessWire;
 +
// v1.0
 +
// This is the template file for main /blog/ page that lists blog post summaries.
 +
// If there are more than 10 posts, it also paginates them.
 +
include("./partials/layout-blocks.inc");
 +
$layoutBlocks = renderLayoutBlocks($page,$additionalHeaderData);
 +
 
 +
$pagerOptions = array(
 +
'listMarkup' => "<ul class='uk-pagination'>{out}</ul>",
 +
);
 +
?>
 +
 
 +
<main id='main' class='uk-container uk-margin uk-margin-large-bottom'>
 +
<div class='uk-grid-large' uk-grid>
 +
 +
<div id='content' class='uk-width-expand'>
 +
<?php
 +
echo ukHeading1(page()->title, 'divider');
 +
$posts = page()->children('limit=8,sort=-date');
 +
$pagination = $posts->renderPager($pagerOptions);
 +
echo($pagination);
 +
echo ukBlogPosts($posts);
 +
?>
 +
</div>
 +
 
 +
<aside id='sidebar'  class='uk-width-1-3@m'>
 +
<?php
 +
$categories = pages()->get('/categories/');
 +
echo '<div class="uk-card uk-card-muted uk-card-hover uk-card-body uk-margin-medium-top">'.ukNav($categories->children, [ 'header' => $categories->title ]).'</div>';
 +
?>
 +
</aside>
 +
 
 +
</div>
 +
</main>
 +
</syntaxhighlight>
 +
 
 +
=== Helper Functions ===
 +
_uikit.php (Auszug)
 +
<syntaxhighlight lang="php">
 +
/*****************************************************************************************
 +
* ProcessWire/Uikit functions for blog support
 +
*
 +
*/
 +
 
 +
/**
 +
* Render a blog post using Uikit “article” component
 +
*
 +
* @param Page $page Blog post
 +
* @param array|string $options Options to modify default behavior
 +
*  - `summarize` (bool): Display blog post summary rather than full post? (default=auto-detect).
 +
*  - `metaIcon` (string): Icon to use for blog meta info in header (default=info).
 +
*  - `moreIcon` (string): Icon to use for more link in summarized blog post (default=more).
 +
*  - `categoryIcon` (string): Icon to use for identification of categories in blog header (default=hashtag).
 +
*  - `bylineText` (string): Template for byline (default=“Posted by %1$s on %2$s”).
 +
* @return string
 +
*
 +
*/
 +
function ukBlogPost(Page $page, $options = array()) {
 +
 
 +
$defaults = array(
 +
'summarize' => null, // Display blog post summary rather than full post? (null=auto-detect)
 +
'metaIcon' => 'info',
 +
'moreIcon' => 'arrow-right',
 +
'moreText' => __('Read more'),
 +
'categoryIcon' => 'hashtag',
 +
//'bylineText' => __('Posted by %1$s on %2$s'),
 +
'bylineText' => '%2$s',
 +
);
 +
 
 +
$options = _ukMergeOptions($defaults, $options);
 +
$title = $page->title;
 +
$date = $page->date ? $page->date : $page->createdStr;
 +
//$created = date('d.m.Y',$page->getUnformatted("date"));
 +
//$created = date('d.m.Y',$page->get("modified"));
 +
$name = $page->createdUser->name;
 +
$body = $page->body;
 +
$metaIcon = ukIcon($options['metaIcon']);
 +
$moreIcon = ukIcon($options['moreIcon']);
 +
$categoryIcon = ukIcon($options['categoryIcon']);
 +
$n = $page->comments->count();
 +
$numComments = $n ? "<a href='$page->url#comments'>" . ukIcon('comments') . " $n</a>" : "";
 +
 
 +
if($options['summarize'] === null) {
 +
// auto-detect: summarize if current page is not the same as the blog post
 +
$options['summarize'] = page()->id != $page->id;
 +
}
 +
 
 +
$categories = $page->categories->each($categoryIcon .
 +
"<a class='uk-button uk-button-text' href='{url}'>{title}</a> "
 +
);
 +
 
 +
if($options['summarize']) {
 +
// link to post in title, and use just the first paragraph in teaser mode
 +
$title = "<a href='$page->url'>$title</a>";
 +
$body = explode('</p>', $body);
 +
$body = reset($body) . ' ';
 +
$body .= "<br><a href='$page->url'>$options[moreText] $moreIcon</a></p>";
 +
$class = 'blog-post-summary';
 +
} else {
 +
$class = 'blog-post-full';
 +
}
 +
 
 +
if($options['summarize']) {
 +
$heading = "<h2 class='uk-margin-remove'>$title</h2>";
 +
} else {
 +
$heading = "<h1 class='uk-article-title uk-margin-remove'>$title</h1>";
 +
}
 +
 
 +
$byline = sprintf($options['bylineText'], $name, $date);
 +
 
 +
// return the blog post article markup
 +
return "
 +
<article class='uk-article blog-post $class'>
 +
$heading
 +
<p class='uk-margin-small'>
 +
<span class='uk-article-meta'>
 +
$metaIcon
 +
$byline
 +
</span>
 +
<span class='categories'>
 +
$categories
 +
</span>
 +
<span class='num-comments uk-margin-small-left uk-text-muted'>
 +
$numComments
 +
</span>
 +
</p>
 +
$body
 +
</article>
 +
<hr>
 +
";
 +
}
 +
 
 +
/**
 +
* Render multiple blog posts summarized
 +
*
 +
* @param PageArray $posts
 +
* @param array|string $options See the ukBlogPost() method for options, plus:
 +
*  - `paginate` (bool): Use pagination when applicable? (default=true)
 +
* @return string
 +
*
 +
*/
 +
function ukBlogPosts(PageArray $posts, $options = array()) {
 +
if(!$posts->count) {
 +
if(input()->pageNum > 1) {
 +
// redirect to first pagination if accessed at an out-of-bounds pagination
 +
session()->redirect(page()->url);
 +
}
 +
return '';
 +
}
 +
$defaults = array(
 +
'paginate' => false
 +
);
 +
$options = _ukMergeOptions($defaults, $options);
 +
$out = "<div class='blog-posts'>";
 +
foreach($posts as $post) {
 +
$out .= ukBlogPost($post, $options);
 +
}
 +
if($options['paginate'] && $posts->getTotal() > $posts->count()) {
 +
$out .= ukPagination($posts);
 +
}
 +
$out .= "</div>";
 +
return $out;
 +
}
 +
 
 +
/*****************************************************************************************
 +
* ProcessWire/Uikit functions for rendering comments and comment forms
 +
*
 +
* Note: comment threads (depth), stars and votes are not yet supported in here.
 +
*
 +
*/
 +
 
 +
/**
 +
* Render a ProcessWire comment using Uikit markup
 +
*
 +
* (work in progress)
 +
*
 +
* @param Comment $comment
 +
* @param array|string $options Coming soon
 +
* @return string
 +
*
 +
*/
 +
function ukComment(Comment $comment, $options = array()) {
 +
 
 +
$defaults = array(
 +
'comments' => null, // instance of CommentArray when called from ukComments()
 +
'depth' => 0,
 +
);
 +
 
 +
// $options = _ukMergeOptions($defaults, $options);
 +
$text = $comment->getFormatted('text');
 +
$cite = $comment->getFormatted('cite');
 +
$website = $comment->getFormatted('website');
 +
$field = $comment->getField();
 +
$page = $comment->getPage();
 +
$classes = array();
 +
$metas = array();
 +
$gravatar = '';
 +
$replies = '';
 +
 
 +
if($field->get('useGravatar')) {
 +
$img = $comment->gravatar($field->get('useGravatar'), $field->get('useGravatarImageset'));
 +
if($img) $gravatar = "<div class='uk-width-auto'><img class='uk-comment-avatar' src='$img' alt='$cite'></div>";
 +
}
 +
 
 +
if($website) $cite = "<a href='$website' rel='nofollow' target='_blank'>$cite</a>";
 +
$created = wireDate('relative', $comment->created);
 +
 
 +
if($field->get('usePermalink')) {
 +
$permalink = $page->httpUrl;
 +
$urlSegmentStr = $this->wire('input')->urlSegmentStr;
 +
if($urlSegmentStr) $permalink .= rtrim($permalink, '/') . $urlSegmentStr . '/';
 +
$permalink .= '#Comment' . $comment->id;
 +
$permalink = "<a href='$permalink'>" . __('Permalink') . "</a>";
 +
$metas[] = "<li>$permalink</li>";
 +
}
 +
 
 +
$classes = implode(' ', $classes);
 +
$metas = implode('', $metas);
 +
 
 +
$out = "
 +
<article id='Comment$comment->id' class='$classes uk-comment uk-comment-primary' data-comment='$comment->id'>
 +
<header class='uk-comment-header uk-grid-medium uk-flex-middle' uk-grid>
 +
$gravatar
 +
<div class='uk-width-expand'>
 +
<h4 class='uk-comment-title uk-margin-remove'>$cite</h4>
 +
<ul class='uk-comment-meta uk-subnav uk-subnav-divider uk-margin-remove-top'>
 +
<li>$created</li>
 +
$metas
 +
</ul>
 +
</div>
 +
</header>
 +
<div class='uk-comment-body'>
 +
$text
 +
</div>
 +
</article>
 +
$replies
 +
";
 +
 
 +
return $out;
 +
}
 +
 
 +
/**
 +
* Render a list of ProcessWire comments using Uikit markup
 +
*
 +
* @param CommentArray $comments
 +
* @param array|string $options Options to modify default behavior
 +
*  - `id` (string): HTML id attribute of the comments list (default='comments').
 +
*  - `parent_id` (int): Database id of the parent comment, when rendering a comment thread.
 +
* @return string
 +
*
 +
*/
 +
function ukComments(CommentArray $comments, $options = array()) {
 +
 
 +
$defaults = array(
 +
'id' => 'comments',
 +
'parent_id' => 0,
 +
'comments' => $comments, // for ukComment() method only
 +
);
 +
 
 +
if(!count($comments)) return '';
 +
$options = _ukMergeOptions($defaults, $options);
 +
 
 +
$out = "<ul id='$options[id]' class='uk-comment-list'>";
 +
 
 +
foreach($comments as $comment) {
 +
$out .= "<li class='uk-margin'>" . ukComment($comment, $options) . "</li>";
 +
}
 +
 
 +
$out .= "</ul>";
 +
 
 +
return $out;
 +
}
 +
 
 +
/**
 +
* Render a comment posting form
 +
*
 +
* @param CommentArray $comments
 +
* @param array $options See `CommentForm` class for all options.
 +
* @return string
 +
*
 +
*/
 +
function ukCommentForm(CommentArray $comments, array $options = array()) {
 +
 
 +
$defaults = array(
 +
'headline' => "",
 +
'successMessage' =>
 +
__('Thank you, your comment has been posted.'),
 +
'pendingMessage' =>
 +
__('Your comment has been submitted and will appear once approved by the moderator.'),
 +
'errorMessage' =>
 +
__('Your comment was not saved due to one or more errors.') . ' ' .
 +
__('Please check that you have completed all fields before submitting again.'),
 +
);
 +
 
 +
$options = _ukMergeOptions($defaults, $options);
 +
$options['successMessage'] = ukAlertSuccess($options['successMessage'], 'check');
 +
$options['pendingMessage'] = ukAlertSuccess($options['pendingMessage'], 'check');
 +
$options['errorMessage'] = ukAlertDanger($options['errorMessage'], 'warning');
 +
 
 +
if(!isset($options['attrs']) || !isset($options['attrs']['class'])) {
 +
$options['attrs'] = array('class' => 'uk-comment uk-comment-primary');
 +
}
 +
 
 +
$adjustments = array(
 +
"<input type='text'" => "<input type='text' class='uk-input'",
 +
"<p class='CommentForm" => "<p class='uk-margin-remove-top CommentForm",
 +
"<textarea " => "<textarea class='uk-textarea' ",
 +
"<button " => "<button class='uk-button uk-button-primary' ",
 +
"<label " => "<label class='uk-form-label' ",
 +
);
 +
 
 +
$out = $comments->renderForm($options);
 +
$out = str_replace(array_keys($adjustments), array_values($adjustments), $out);
 +
 
 +
return $out;
 +
}
 +
 
 +
</syntaxhighlight>
 +
_helpers.php
 +
<syntaxhighlight lang="php">
 +
function truncateText($text,$characters=500){
 +
$summary = strip_tags($text);
 +
if(strlen($summary) > $characters) {
 +
$summary = substr($summary, 0, $characters); // display no more than 500 chars
 +
$trimToSentence = substr($summary, 0, strrpos($summary, ". ")+1); // and truncate to last sentence
 +
if( strlen( $trimToSentence) > intval($characters/3) ) $summary = $trimToSentence; // use it if not too short
 +
}
 +
$summary = trim($summary);
 +
return $summary;
 +
}
 +
</syntaxhighlight>

Aktuelle Version vom 26. Mai 2021, 16:12 Uhr

Wie erstellt man einen Blog oder eine News-Section in ProcessWire ?

ProcessBlog Modul von Kongondo[Bearbeiten]

Zu viel Zeug drin ;-)

https://processwire.com/modules/process-blog/
ProcessBlog

Eigenes Blog Modul erstellen[Bearbeiten]

Todo

Beispiel mit PageReference für Kategorien[Bearbeiten]

Paging[Bearbeiten]

Module[Bearbeiten]

  • Comments / Kommentare (Core)
  • InputfieldCommentsAdmin
  • FieldtypeComments
https://processwire.com/docs/modules/guides/comments/

Felder[Bearbeiten]

  • date (Datum des Blog-Eintrags)
  • comments (Verwaltet Kommentare für eine Seite. Hier für einen Blog-Eintrag)
  • tags (Legt die Tags/Kategorien für einen Blogeintrag fest) - Hier mußt du noch die Parent Page festlegen
  • pr_single (Legt die Seite für die Einzelansicht in der Blog-Seite fest)

Templates[Bearbeiten]

  • tag (steht für eine Blog-Kategorie)
  • tags (unterhalb dieser Seite kann man nur Kategorien / Tags anlegen)
  • blog (Gibt den Blog aus)
  • blog-post (Steht für einen einzelnen Blogeintrag und für die Einzelansicht)

Rechte[Bearbeiten]

Benutzerrolle: editor

  • category.add
  • category.edit
  • category.create

Kopiervorlage[Bearbeiten]

Felder[Bearbeiten]

{
    "comments": {
        "id": 134,
        "name": "comments",
        "label": "Comments",
        "flags": 0,
        "type": "FieldtypeComments",
        "schemaVersion": 6,
        "moderate": 1,
        "redirectAfterPost": 1,
        "quietSave": 1,
        "deleteSpamDays": 3,
        "useWebsite": 1,
        "dateFormat": "relative",
        "useGravatar": "g",
        "tags": "blog",
        "icon": "comment-o",
        "notificationEmail": "",
        "fromEmail": "",
        "notifySpam": "",
        "useNotify": 0,
        "useAkismet": "",
        "depth": 0,
        "sortNewest": "",
        "useVotes": 0,
        "useStars": 0,
        "collapsed": 0,
        "showIf": "",
        "themeOffset": "",
        "themeBorder": "",
        "themeColor": "",
        "columnWidth": 100,
        "required": "",
        "requiredIf": ""
    },
    "date": {
        "id": 133,
        "name": "date",
        "label": "Date",
        "flags": 0,
        "type": "FieldtypeDatetime",
        "dateOutputFormat": "j.n.Y",
        "size": 25,
        "datepicker": 3,
        "dateInputFormat": "d.m.Y",
        "defaultToday": 1,
        "placeholder": "yyyy/mm/dd",
        "icon": "calendar",
        "tags": "blog",
        "inputType": "text",
        "dateSelectFormat": "yMd",
        "yearFrom": 1921,
        "yearTo": 2041,
        "htmlType": "date",
        "collapsed": 0,
        "timeInputSelect": 0,
        "yearLock": 0,
        "columnWidth": 20,
        "showIf": "",
        "themeOffset": "",
        "themeBorder": "",
        "themeColor": "",
        "required": "",
        "requiredAttr": "",
        "requiredIf": "",
        "dateMin": "",
        "dateMax": "",
        "timeStep": "",
        "timeMin": "",
        "timeMax": "",
        "timeInputFormat": "",
        "yearRange": ""
    }
}

Templates[Bearbeiten]

{
    "blog": {
        "id": 76,
        "name": "blog",
        "fieldgroups_id": "blog",
        "flags": 0,
        "cache_time": 0,
        "useRoles": 1,
        "editRoles": [
            "editor"
        ],
        "addRoles": [
            "editor"
        ],
        "createRoles": [
            "editor"
        ],
        "rolesPermissions": [],
        "noInherit": 0,
        "childrenTemplatesID": 0,
        "sortfield": "",
        "noChildren": "",
        "noParents": "",
        "childTemplates": [],
        "parentTemplates": [],
        "allowPageNum": 1,
        "allowChangeUser": 0,
        "redirectLogin": 0,
        "urlSegments": 1,
        "https": 0,
        "slashUrls": 1,
        "slashPageNum": 0,
        "slashUrlSegments": 0,
        "altFilename": "",
        "guestSearchable": 0,
        "pageClass": "",
        "childNameFormat": "",
        "pageLabelField": "",
        "noGlobal": 0,
        "noMove": 0,
        "noTrash": 0,
        "noSettings": 0,
        "noChangeTemplate": 0,
        "noShortcut": 0,
        "noUnpublish": 0,
        "noLang": 0,
        "compile": 3,
        "nameContentTab": 0,
        "noCacheGetVars": "",
        "noCachePostVars": "",
        "useCacheForUsers": 0,
        "cacheExpire": 0,
        "cacheExpirePages": [],
        "cacheExpireSelector": "",
        "label": "Blog",
        "tags": "",
        "titleNames": 0,
        "noPrependTemplateFile": 0,
        "noAppendTemplateFile": 0,
        "prependFile": "",
        "appendFile": "",
        "pagefileSecure": false,
        "tabContent": "",
        "tabChildren": "",
        "nameLabel": "",
        "contentType": "",
        "errorAction": 0,
        "connectedFieldID": null,
        "ns": "ProcessWire",
        "_exportMode": true,
        "roles": [
            "guest",
            "editor"
        ],
        "fieldgroupFields": [
            "title",
            "menus",
            "pr_single"
        ],
        "fieldgroupContexts": {
            "title": [],
            "menus": [],
            "pr_single": {
                "columnWidth": 50,
                "label": "Kategorien",
                "notes": "Wähle die Seite aus, welche die Kategorien enthält."
            }
        }
    },
    "blog-post": {
        "id": 74,
        "name": "blog-post",
        "fieldgroups_id": "blog-post",
        "flags": 0,
        "cache_time": -604800,
        "useRoles": 0,
        "noInherit": 1,
        "childrenTemplatesID": 0,
        "sortfield": "",
        "noChildren": "",
        "noParents": "",
        "childTemplates": [],
        "parentTemplates": [],
        "allowPageNum": 0,
        "allowChangeUser": 0,
        "redirectLogin": 0,
        "urlSegments": 0,
        "https": 0,
        "slashUrls": 1,
        "slashPageNum": 0,
        "slashUrlSegments": 0,
        "altFilename": "",
        "guestSearchable": 0,
        "pageClass": "",
        "childNameFormat": "",
        "pageLabelField": "",
        "noGlobal": 0,
        "noMove": 0,
        "noTrash": 0,
        "noSettings": 0,
        "noChangeTemplate": 0,
        "noShortcut": 0,
        "noUnpublish": 0,
        "noLang": 0,
        "compile": 3,
        "nameContentTab": 0,
        "noCacheGetVars": "",
        "noCachePostVars": "",
        "useCacheForUsers": 0,
        "cacheExpire": 0,
        "cacheExpirePages": [],
        "cacheExpireSelector": "",
        "label": "Blog-Post (News)",
        "tags": "",
        "titleNames": 0,
        "noPrependTemplateFile": 0,
        "noAppendTemplateFile": 0,
        "prependFile": "",
        "appendFile": "",
        "pagefileSecure": false,
        "tabContent": "",
        "tabChildren": "",
        "nameLabel": "",
        "contentType": "",
        "errorAction": 0,
        "connectedFieldID": null,
        "ns": "ProcessWire",
        "_exportMode": true,
        "fieldgroupFields": [
            "title",
            "date",
            "body",
            "images",
            "files",
            "categories",
            "comments"
        ],
        "fieldgroupContexts": {
            "title": [],
            "date": {
                "columnWidth": 25
            },
            "body": [],
            "images": [],
            "files": [],
            "categories": [],
            "comments": []
        }
    },
    "categories": {
        "id": 77,
        "name": "categories",
        "fieldgroups_id": "categories",
        "flags": 0,
        "cache_time": 0,
        "useRoles": 1,
        "editRoles": [
            "editor"
        ],
        "addRoles": [
            "editor"
        ],
        "createRoles": [
            "editor"
        ],
        "rolesPermissions": [],
        "noInherit": 0,
        "childrenTemplatesID": 0,
        "sortfield": "",
        "noChildren": "",
        "noParents": "",
        "childTemplates": [
            "category"
        ],
        "parentTemplates": [],
        "allowPageNum": 0,
        "allowChangeUser": 0,
        "redirectLogin": 0,
        "urlSegments": 0,
        "https": 0,
        "slashUrls": 1,
        "slashPageNum": 0,
        "slashUrlSegments": 0,
        "altFilename": "",
        "guestSearchable": 0,
        "pageClass": "",
        "childNameFormat": "",
        "pageLabelField": "",
        "noGlobal": 0,
        "noMove": 0,
        "noTrash": 0,
        "noSettings": 0,
        "noChangeTemplate": 0,
        "noShortcut": 0,
        "noUnpublish": 0,
        "noLang": 0,
        "compile": 3,
        "nameContentTab": 0,
        "noCacheGetVars": "",
        "noCachePostVars": "",
        "useCacheForUsers": 0,
        "cacheExpire": 0,
        "cacheExpirePages": [],
        "cacheExpireSelector": "",
        "label": "Kategorien",
        "tags": "",
        "titleNames": 0,
        "noPrependTemplateFile": 0,
        "noAppendTemplateFile": 0,
        "prependFile": "",
        "appendFile": "",
        "pagefileSecure": false,
        "tabContent": "",
        "tabChildren": "",
        "nameLabel": "",
        "contentType": "",
        "errorAction": 0,
        "connectedFieldID": null,
        "ns": "ProcessWire",
        "_exportMode": true,
        "roles": [
            "guest",
            "editor"
        ],
        "fieldgroupFields": [
            "title"
        ],
        "fieldgroupContexts": {
            "title": []
        }
    },
    "category": {
        "id": 75,
        "name": "category",
        "fieldgroups_id": "category",
        "flags": 0,
        "cache_time": 0,
        "useRoles": 1,
        "editRoles": [
            "editor"
        ],
        "addRoles": [
            "editor"
        ],
        "createRoles": [
            "editor"
        ],
        "rolesPermissions": [],
        "noInherit": 0,
        "childrenTemplatesID": 0,
        "sortfield": "",
        "noChildren": "",
        "noParents": "",
        "childTemplates": [],
        "parentTemplates": [
            "categories"
        ],
        "allowPageNum": 0,
        "allowChangeUser": 0,
        "redirectLogin": 0,
        "urlSegments": 0,
        "https": 0,
        "slashUrls": 1,
        "slashPageNum": 0,
        "slashUrlSegments": 0,
        "altFilename": "",
        "guestSearchable": 0,
        "pageClass": "",
        "childNameFormat": "",
        "pageLabelField": "",
        "noGlobal": 0,
        "noMove": 0,
        "noTrash": 0,
        "noSettings": 0,
        "noChangeTemplate": 0,
        "noShortcut": 0,
        "noUnpublish": 0,
        "noLang": 0,
        "compile": 3,
        "nameContentTab": 0,
        "noCacheGetVars": "",
        "noCachePostVars": "",
        "useCacheForUsers": 0,
        "cacheExpire": 0,
        "cacheExpirePages": [],
        "cacheExpireSelector": "",
        "label": "",
        "tags": "",
        "titleNames": 0,
        "noPrependTemplateFile": 0,
        "noAppendTemplateFile": 0,
        "prependFile": "",
        "appendFile": "",
        "pagefileSecure": false,
        "tabContent": "",
        "tabChildren": "",
        "nameLabel": "",
        "contentType": "",
        "errorAction": 0,
        "connectedFieldID": null,
        "ns": "ProcessWire",
        "_exportMode": true,
        "roles": [
            "guest",
            "editor"
        ],
        "fieldgroupFields": [
            "title"
        ],
        "fieldgroupContexts": {
            "title": []
        }
    }
}

Beispiel (Anlehnung an Ryans Blog Beispiel)[Bearbeiten]

Templates[Bearbeiten]

blog.php

<?php namespace ProcessWire;
// v1.0
// This is the template file for main /blog/ page that lists blog post summaries.
// If there are more than 10 posts, it also paginates them.
include("./partials/layout-blocks.inc");
$layoutBlocks = renderLayoutBlocks($page,$additionalHeaderData);

$pagerOptions = array(
	 'listMarkup' => "<ul class='uk-pagination'>{out}</ul>",
);
?>

<main id='main' class='uk-container uk-margin uk-margin-large-bottom'>
	<div class='uk-grid-large' uk-grid>
		
		<div id='content' class='uk-width-expand'>
			<?php
			echo ukHeading1(page()->title, 'divider');
			$posts = page()->children('limit=8,sort=-date');
			$pagination = $posts->renderPager($pagerOptions);
			echo($pagination);
			echo ukBlogPosts($posts);
			?>
		</div>

		<aside id='sidebar'  class='uk-width-1-3@m'>
			<?php
			$categories = pages()->get('/categories/');
			echo '<div class="uk-card uk-card-muted uk-card-hover uk-card-body uk-margin-medium-top">'.ukNav($categories->children, [ 'header' => $categories->title ]).'</div>';
			?>
		</aside>

	</div>
</main>

Helper Functions[Bearbeiten]

_uikit.php (Auszug)

/*****************************************************************************************
 * ProcessWire/Uikit functions for blog support
 *
 */

/**
 * Render a blog post using Uikit “article” component
 *
 * @param Page $page Blog post
 * @param array|string $options Options to modify default behavior
 *  - `summarize` (bool): Display blog post summary rather than full post? (default=auto-detect).
 *  - `metaIcon` (string): Icon to use for blog meta info in header (default=info).
 *  - `moreIcon` (string): Icon to use for more link in summarized blog post (default=more).
 *  - `categoryIcon` (string): Icon to use for identification of categories in blog header (default=hashtag).
 *  - `bylineText` (string): Template for byline (default=“Posted by %1$s on %2$s”).
 * @return string
 *
 */
function ukBlogPost(Page $page, $options = array()) {

	$defaults = array(
		'summarize' => null, // Display blog post summary rather than full post? (null=auto-detect)
		'metaIcon' => 'info',
		'moreIcon' => 'arrow-right',
		'moreText' => __('Read more'),
		'categoryIcon' => 'hashtag',
		//'bylineText' => __('Posted by %1$s on %2$s'),
		'bylineText' => '%2$s',
	);

	$options = _ukMergeOptions($defaults, $options);
	$title = $page->title;
	$date = $page->date ? $page->date : $page->createdStr;
	//$created = date('d.m.Y',$page->getUnformatted("date"));
	//$created = date('d.m.Y',$page->get("modified"));
	$name = $page->createdUser->name;
	$body = $page->body;
	$metaIcon = ukIcon($options['metaIcon']);
	$moreIcon = ukIcon($options['moreIcon']);
	$categoryIcon = ukIcon($options['categoryIcon']);
	$n = $page->comments->count();
	$numComments = $n ? "<a href='$page->url#comments'>" . ukIcon('comments') . " $n</a>" : "";

	if($options['summarize'] === null) {
		// auto-detect: summarize if current page is not the same as the blog post
		$options['summarize'] = page()->id != $page->id;
	}

	$categories = $page->categories->each($categoryIcon .
		"<a class='uk-button uk-button-text' href='{url}'>{title}</a> "
	);

	if($options['summarize']) {
		// link to post in title, and use just the first paragraph in teaser mode
		$title = "<a href='$page->url'>$title</a>";
		$body = explode('</p>', $body);
		$body = reset($body) . ' ';
		$body .= "<br><a href='$page->url'>$options[moreText] $moreIcon</a></p>";
		$class = 'blog-post-summary';
	} else {
		$class = 'blog-post-full';
	}

	if($options['summarize']) {
		$heading = "<h2 class='uk-margin-remove'>$title</h2>";
	} else {
		$heading = "<h1 class='uk-article-title uk-margin-remove'>$title</h1>";
	}

	$byline = sprintf($options['bylineText'], $name, $date);

	// return the blog post article markup
	return "
		<article class='uk-article blog-post $class'>
			$heading
			<p class='uk-margin-small'>
			<span class='uk-article-meta'>
				$metaIcon
				$byline
			</span>
			<span class='categories'>
				$categories
			</span>
			<span class='num-comments uk-margin-small-left uk-text-muted'>
				$numComments
			</span>
			</p>
			$body
		</article>
		<hr>
	";
}

/**
 * Render multiple blog posts summarized
 *
 * @param PageArray $posts
 * @param array|string $options See the ukBlogPost() method for options, plus:
 *  - `paginate` (bool): Use pagination when applicable? (default=true)
 * @return string
 *
 */
function ukBlogPosts(PageArray $posts, $options = array()) {
	if(!$posts->count) {
		if(input()->pageNum > 1) {
			// redirect to first pagination if accessed at an out-of-bounds pagination
			session()->redirect(page()->url);
		}
		return '';
	}
	$defaults = array(
		'paginate' => false
	);
	$options = _ukMergeOptions($defaults, $options);
	$out = "<div class='blog-posts'>";
	foreach($posts as $post) {
		$out .= ukBlogPost($post, $options);
	}
	if($options['paginate'] && $posts->getTotal() > $posts->count()) {
		$out .= ukPagination($posts);
	}
	$out .= "</div>";
	return $out;
}

/*****************************************************************************************
 * ProcessWire/Uikit functions for rendering comments and comment forms
 *
 * Note: comment threads (depth), stars and votes are not yet supported in here.
 *
 */

/**
 * Render a ProcessWire comment using Uikit markup
 *
 * (work in progress)
 *
 * @param Comment $comment
 * @param array|string $options Coming soon
 * @return string
 *
 */
function ukComment(Comment $comment, $options = array()) {

	$defaults = array(
		'comments' => null, // instance of CommentArray when called from ukComments()
		'depth' => 0,
	);

	// $options = _ukMergeOptions($defaults, $options);
	$text = $comment->getFormatted('text');
	$cite = $comment->getFormatted('cite');
	$website = $comment->getFormatted('website');
	$field = $comment->getField();
	$page = $comment->getPage();
	$classes = array();
	$metas = array();
	$gravatar = '';
	$replies = '';

	if($field->get('useGravatar')) {
		$img = $comment->gravatar($field->get('useGravatar'), $field->get('useGravatarImageset'));
		if($img) $gravatar = "<div class='uk-width-auto'><img class='uk-comment-avatar' src='$img' alt='$cite'></div>";
	}

	if($website) $cite = "<a href='$website' rel='nofollow' target='_blank'>$cite</a>";
	$created = wireDate('relative', $comment->created);

	if($field->get('usePermalink')) {
		$permalink = $page->httpUrl;
		$urlSegmentStr = $this->wire('input')->urlSegmentStr;
		if($urlSegmentStr) $permalink .= rtrim($permalink, '/') . $urlSegmentStr . '/';
		$permalink .= '#Comment' . $comment->id;
		$permalink = "<a href='$permalink'>" . __('Permalink') . "</a>";
		$metas[] = "<li>$permalink</li>";
	}

	$classes = implode(' ', $classes);
	$metas = implode('', $metas);

	$out = "
		<article id='Comment$comment->id' class='$classes uk-comment uk-comment-primary' data-comment='$comment->id'>
			<header class='uk-comment-header uk-grid-medium uk-flex-middle' uk-grid>
				$gravatar
				<div class='uk-width-expand'>
					<h4 class='uk-comment-title uk-margin-remove'>$cite</h4>
					<ul class='uk-comment-meta uk-subnav uk-subnav-divider uk-margin-remove-top'>
						<li>$created</li>
						$metas
					</ul>
				</div>
			</header>
			<div class='uk-comment-body'>
				$text
			</div>
		</article>
		$replies
	";

	return $out;
}

/**
 * Render a list of ProcessWire comments using Uikit markup
 *
 * @param CommentArray $comments
 * @param array|string $options Options to modify default behavior
 *  - `id` (string): HTML id attribute of the comments list (default='comments').
 *  - `parent_id` (int): Database id of the parent comment, when rendering a comment thread.
 * @return string
 *
 */
function ukComments(CommentArray $comments, $options = array()) {

	$defaults = array(
		'id' => 'comments',
		'parent_id' => 0,
		'comments' => $comments, // for ukComment() method only
	);

	if(!count($comments)) return '';
	$options = _ukMergeOptions($defaults, $options);

	$out = "<ul id='$options[id]' class='uk-comment-list'>";

	foreach($comments as $comment) {
		$out .= "<li class='uk-margin'>" . ukComment($comment, $options) . "</li>";
	}

	$out .= "</ul>";

	return $out;
}

/**
 * Render a comment posting form
 *
 * @param CommentArray $comments
 * @param array $options See `CommentForm` class for all options.
 * @return string
 *
 */
function ukCommentForm(CommentArray $comments, array $options = array()) {

	$defaults = array(
		'headline' => "",
		'successMessage' =>
			__('Thank you, your comment has been posted.'),
		'pendingMessage' =>
			__('Your comment has been submitted and will appear once approved by the moderator.'),
		'errorMessage' =>
			__('Your comment was not saved due to one or more errors.') . ' ' .
			__('Please check that you have completed all fields before submitting again.'),
	);

	$options = _ukMergeOptions($defaults, $options);
	$options['successMessage'] = ukAlertSuccess($options['successMessage'], 'check');
	$options['pendingMessage'] = ukAlertSuccess($options['pendingMessage'], 'check');
	$options['errorMessage'] = ukAlertDanger($options['errorMessage'], 'warning');

	if(!isset($options['attrs']) || !isset($options['attrs']['class'])) {
		$options['attrs'] = array('class' => 'uk-comment uk-comment-primary');
	}

	$adjustments = array(
		"<input type='text'" => "<input type='text' class='uk-input'",
		"<p class='CommentForm" => "<p class='uk-margin-remove-top CommentForm",
		"<textarea " => "<textarea class='uk-textarea' ",
		"<button " => "<button class='uk-button uk-button-primary' ",
		"<label " => "<label class='uk-form-label' ",
	);

	$out = $comments->renderForm($options);
	$out = str_replace(array_keys($adjustments), array_values($adjustments), $out);

	return $out;
}

_helpers.php

function truncateText($text,$characters=500){
		$summary = strip_tags($text);
		if(strlen($summary) > $characters) {
			$summary = substr($summary, 0, $characters); // display no more than 500 chars
			$trimToSentence = substr($summary, 0, strrpos($summary, ". ")+1); // and truncate to last sentence
			if( strlen( $trimToSentence) > intval($characters/3) ) $summary = $trimToSentence; // use it if not too short
		}
		$summary = trim($summary);
		return $summary;
}