ProcessWire - Blog: Unterschied zwischen den Versionen

Aus Wikizone
Wechseln zu: Navigation, Suche
Zeile 16: Zeile 16:
 
* date (Datum des Blog-Eintrags)
 
* date (Datum des Blog-Eintrags)
 
* comments (Verwaltet Kommentare für eine Seite. Hier für einen Blog-Eintrag)
 
* comments (Verwaltet Kommentare für eine Seite. Hier für einen Blog-Eintrag)
* categories (Legt die Kategorien für einen Blogeintrag fest) - '''Hier mußt du noch die Parent Page festlegen'''
+
* 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)
 
* pr_single (Legt die Seite für die Einzelansicht in der Blog-Seite fest)
  

Version vom 26. Mai 2021, 12:27 Uhr

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

ProcessBlog Modul von Kongondo

Zu viel Zeug drin ;-)

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

Eigenes Blog Modul erstellen

Todo

Beispiel mit PageReference für Kategorien

Module

  • Comments / Kommentare (Core)

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

{
    "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

{
    "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)

Templates

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

_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;
}