[TASK] Initial implementation
3
.gitattributes
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/Build export-ignore
|
||||
/.gitattributes export-ignore
|
||||
/.gitignore export-ignore
|
||||
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
.idea/
|
||||
Build/
|
||||
85
Build/.stylelintrc
Normal file
@ -0,0 +1,85 @@
|
||||
{
|
||||
"plugins": [
|
||||
"stylelint-order"
|
||||
],
|
||||
"rules": {
|
||||
"at-rule-empty-line-before": [
|
||||
"always",
|
||||
{
|
||||
"except": [
|
||||
"blockless-after-same-name-blockless",
|
||||
"first-nested"
|
||||
],
|
||||
"ignore": [
|
||||
"after-comment"
|
||||
]
|
||||
}
|
||||
],
|
||||
"block-no-empty": true,
|
||||
"color-hex-length": "short",
|
||||
"color-no-invalid-hex": true,
|
||||
"comment-empty-line-before": [
|
||||
"always",
|
||||
{
|
||||
"except": [
|
||||
"first-nested"
|
||||
],
|
||||
"ignore": [
|
||||
"stylelint-commands"
|
||||
]
|
||||
}
|
||||
],
|
||||
"comment-no-empty": true,
|
||||
"comment-whitespace-inside": "always",
|
||||
"custom-property-empty-line-before": "never",
|
||||
"declaration-block-no-duplicate-properties": true,
|
||||
"declaration-block-no-redundant-longhand-properties": true,
|
||||
"declaration-block-no-shorthand-property-overrides": true,
|
||||
"declaration-block-single-line-max-declarations": 1,
|
||||
"declaration-empty-line-before": "never",
|
||||
"font-family-no-duplicate-names": true,
|
||||
"function-calc-no-unspaced-operator": true,
|
||||
"function-linear-gradient-no-nonstandard-direction": true,
|
||||
"function-name-case": "lower",
|
||||
"keyframe-declaration-no-important": true,
|
||||
"length-zero-no-unit": true,
|
||||
"media-feature-name-no-unknown": true,
|
||||
"no-empty-source": true,
|
||||
"no-invalid-double-slash-comments": true,
|
||||
"property-no-unknown": true,
|
||||
"rule-empty-line-before": [
|
||||
"always-multi-line",
|
||||
{
|
||||
"except": [
|
||||
"first-nested"
|
||||
],
|
||||
"ignore": [
|
||||
"after-comment"
|
||||
]
|
||||
}
|
||||
],
|
||||
"selector-pseudo-class-no-unknown": true,
|
||||
"selector-pseudo-element-colon-notation": "single",
|
||||
"selector-pseudo-element-no-unknown": true,
|
||||
"selector-type-case": "lower",
|
||||
"selector-type-no-unknown": [
|
||||
true,
|
||||
{
|
||||
"ignore": [
|
||||
"custom-elements"
|
||||
]
|
||||
}
|
||||
],
|
||||
"shorthand-property-no-redundant-values": true,
|
||||
"string-no-newline": true,
|
||||
"unit-no-unknown": true,
|
||||
"order/order": [
|
||||
"custom-properties",
|
||||
"declarations"
|
||||
],
|
||||
"order/properties-order": [
|
||||
"width",
|
||||
"height"
|
||||
]
|
||||
}
|
||||
}
|
||||
15
Build/Makefile
Normal file
@ -0,0 +1,15 @@
|
||||
.PHONY: install
|
||||
install:
|
||||
ddev npm install
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
ddev npm run build:css
|
||||
|
||||
.PHONY: compile
|
||||
compile:
|
||||
ddev npm run compile:css
|
||||
|
||||
.PHONY: watch
|
||||
watch:
|
||||
ddev npm run watch:css
|
||||
8
Build/Sources/Css/pagelayout.css
Normal file
@ -0,0 +1,8 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
.shadow-component {
|
||||
-webkit-box-shadow: var(--typo3-component-box-shadow);
|
||||
box-shadow: var(--typo3-component-box-shadow);
|
||||
}
|
||||
3523
Build/package-lock.json
generated
Normal file
29
Build/package.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "ew_bloggy",
|
||||
"description": "Bundles all resources for the usage",
|
||||
"repository": "https://gitea.fischer.im/evoWeb/ew_bloggy.git",
|
||||
"readme": "../README.md",
|
||||
"homepage": "https://www.evoweb.de/",
|
||||
"author": "Sebastian Fischer",
|
||||
"version": "0.0.1",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"engines": {
|
||||
"node": ">=22.0.0 <23.0.0",
|
||||
"npm": ">=10.0.0"
|
||||
},
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"css-minify": "^2.1.0",
|
||||
"stylelint": "^16.13.2",
|
||||
"stylelint-order": "^6.0.4",
|
||||
"tailwindcss": "^3.4.17"
|
||||
},
|
||||
"scripts": {
|
||||
"build:css": "npm run lint:css && npm run compile:css && npm run minify:css && npm run remove:css",
|
||||
"lint:css": "stylelint Sources/Css/*.css",
|
||||
"compile:css": "tailwindcss -i ./Sources/Css/pagelayout.css -o ../Resources/Public/Css/pagelayout.css",
|
||||
"minify:css": "css-minify -f ../Resources/Public/Css/pagelayout.css -o ../Resources/Public/Css/",
|
||||
"remove:css": "rm ../Resources/Public/Css/pagelayout.css",
|
||||
"watch:css": "tailwindcss -i ./Sources/Css/pagelayout.css -o ../Resources/Public/Css/pagelayout.css --watch"
|
||||
}
|
||||
}
|
||||
35
Build/tailwind.config.js
Normal file
@ -0,0 +1,35 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
prefix: 'tw-',
|
||||
corePlugins: {
|
||||
preflight: false,
|
||||
},
|
||||
content: [
|
||||
'../Resources/Private/Templates/PageLayout/*.html'
|
||||
],
|
||||
plugins: [],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
component: {
|
||||
bg: 'var(--typo3-component-bg)',
|
||||
},
|
||||
},
|
||||
borderRadius: {
|
||||
component: 'var(--typo3-component-border-radius)',
|
||||
},
|
||||
borderWidth: {
|
||||
12: '12px',
|
||||
},
|
||||
gridTemplateColumns: {
|
||||
component: 'repeat(auto-fit, minmax(150px, 1fr))',
|
||||
},
|
||||
margin: {
|
||||
component: 'var(--typo3-spacing, 1em) auto',
|
||||
},
|
||||
spacing: {
|
||||
19: '4.75rem',
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
87
Classes/Backend/View/PostHeaderRenderer.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is developed by evoWeb.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Evoweb\EwBloggy\Backend\View;
|
||||
|
||||
use Evoweb\EwBloggy\Constants;
|
||||
use Evoweb\EwBloggy\Domain\Model\Post;
|
||||
use Evoweb\EwBloggy\Domain\Repository\PostRepository;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\Type\Bitmask\Permission;
|
||||
use TYPO3\CMS\Core\View\ViewFactoryData;
|
||||
use TYPO3\CMS\Core\View\ViewFactoryInterface;
|
||||
use TYPO3\CMS\Fluid\View\FluidViewAdapter;
|
||||
|
||||
readonly class PostHeaderRenderer
|
||||
{
|
||||
public function __construct(
|
||||
private ViewFactoryInterface $viewFactory,
|
||||
private PostRepository $postRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
public function render(ServerRequestInterface $request): string
|
||||
{
|
||||
$pageUid = (int)($request->getParsedBody()['id'] ?? $request->getQueryParams()['id'] ?? 0);
|
||||
$pageInfo = $this->getPageRecord($pageUid);
|
||||
if ($pageInfo === null || $pageInfo['doktype'] !== Constants::DOKTYPE_BLOG_POST) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$post = $this->getPostByUid($pageUid);
|
||||
$view = $this->getView($request);
|
||||
$view->assign('post', $post);
|
||||
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
protected function getPostByUid(int $pageUid): Post
|
||||
{
|
||||
$query = $this->postRepository->createQuery();
|
||||
$querySettings = $query->getQuerySettings();
|
||||
$querySettings->setIgnoreEnableFields(true);
|
||||
$querySettings->setRespectStoragePage(false);
|
||||
$this->postRepository->setDefaultQuerySettings($querySettings);
|
||||
return $this->postRepository->findOneBy(['uid' => $pageUid]);
|
||||
}
|
||||
|
||||
protected function getPageRecord(int $pageUid): ?array
|
||||
{
|
||||
$pagePermsClause = $this->getBackendUser()?->getPagePermsClause(Permission::PAGE_SHOW) ?? '';
|
||||
return BackendUtility::getRecord('pages', $pageUid, 'doktype', $pagePermsClause);
|
||||
}
|
||||
|
||||
protected function getView(ServerRequestInterface $request): FluidViewAdapter
|
||||
{
|
||||
$viewFactoryData = new ViewFactoryData(
|
||||
['EXT:ew_bloggy/Resources/Private/Templates'],
|
||||
['EXT:ew_bloggy/Resources/Private/Partials'],
|
||||
['EXT:ew_bloggy/Resources/Private/Layouts'],
|
||||
request: $request,
|
||||
);
|
||||
/** @var FluidViewAdapter $view */
|
||||
$view = $this->viewFactory->create($viewFactoryData);
|
||||
$view->getRenderingContext()->setControllerName('PageLayout');
|
||||
$view->getRenderingContext()->setControllerAction('Header');
|
||||
return $view;
|
||||
}
|
||||
|
||||
protected function getBackendUser(): ?BackendUserAuthentication
|
||||
{
|
||||
return $GLOBALS['BE_USER'] ?? null;
|
||||
}
|
||||
}
|
||||
23
Classes/Constants.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is developed by evoWeb.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Evoweb\EwBloggy;
|
||||
|
||||
class Constants
|
||||
{
|
||||
public const DOKTYPE_BLOG_POST = 137;
|
||||
|
||||
public const CATEGORY_TYPE_BLOG = 100;
|
||||
}
|
||||
126
Classes/Controller/PostController.php
Normal file
@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the package t3g/blog.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Evoweb\EwBloggy\Controller;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Evoweb\EwBloggy\Domain\Repository\PostRepository;
|
||||
use Evoweb\EwBloggy\Pagination\BlogPagination;
|
||||
use T3G\AgencyPack\Blog\Service\MetaTagService;
|
||||
use T3G\AgencyPack\Blog\Utility\ArchiveUtility;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||
use TYPO3\CMS\Extbase\Pagination\QueryResultPaginator;
|
||||
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
|
||||
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
|
||||
|
||||
class PostController extends ActionController
|
||||
{
|
||||
public function __construct(protected PostRepository $postRepository)
|
||||
{
|
||||
}
|
||||
|
||||
protected function initializeView(): void
|
||||
{
|
||||
foreach (($this->settings['flexform'] ?? []) as $key => $value) {
|
||||
if (!empty($value)) {
|
||||
$this->settings[$key] = $value;
|
||||
}
|
||||
}
|
||||
if (isset($this->settings['flexform'])) {
|
||||
unset($this->settings['flexform']);
|
||||
}
|
||||
$this->view->assign('settings', $this->settings);
|
||||
$this->view->assign('data', $this->request->getAttribute('currentContentObject')?->data ?? null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a list of recent posts.
|
||||
*/
|
||||
public function listRecentPostsAction(int $currentPage = 1): ResponseInterface
|
||||
{
|
||||
$maximumItems = (int) ($this->settings['lists']['posts']['maximumDisplayedItems'] ?? 0);
|
||||
$posts = (0 === $maximumItems)
|
||||
? $this->postRepository->findAll()
|
||||
: $this->postRepository->findAllWithLimit($maximumItems);
|
||||
$pagination = $this->getPagination($posts, $currentPage);
|
||||
|
||||
$this->view->assign('posts', $posts);
|
||||
$this->view->assign('pagination', $pagination);
|
||||
return $this->htmlResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a number of latest posts.
|
||||
*/
|
||||
public function listLatestPostsAction(): ResponseInterface
|
||||
{
|
||||
$limit = (int) ($this->settings['limit'] ?? 3);
|
||||
$posts = $this->postRepository->findAllWithLimit($limit);
|
||||
$this->view->assign('posts', $posts);
|
||||
return $this->htmlResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show posts beyond a certain date
|
||||
*/
|
||||
public function listPostsByDateAction(
|
||||
?int $year = null,
|
||||
?int $month = null,
|
||||
int $currentPage = 1
|
||||
): ResponseInterface {
|
||||
if ($year === null) {
|
||||
$posts = $this->postRepository->findMonthsAndYearsWithPosts();
|
||||
$this->view->assign('archiveData', ArchiveUtility::extractDataFromPosts($posts));
|
||||
} else {
|
||||
$dateTime = new DateTimeImmutable(sprintf('%d-%d-1', $year, $month ?? 1));
|
||||
$posts = $this->postRepository->findByMonthAndYear($year, $month);
|
||||
$pagination = $this->getPagination($posts, $currentPage);
|
||||
$this->view->assign('type', 'bydate');
|
||||
$this->view->assign('month', $month);
|
||||
$this->view->assign('year', $year);
|
||||
$this->view->assign('timestamp', $dateTime->getTimestamp());
|
||||
$this->view->assign('posts', $posts);
|
||||
$this->view->assign('pagination', $pagination);
|
||||
$title = str_replace(
|
||||
[
|
||||
'###MONTH###',
|
||||
'###MONTH_NAME###',
|
||||
'###YEAR###',
|
||||
],
|
||||
[
|
||||
$month,
|
||||
$dateTime->format('F'),
|
||||
$year,
|
||||
],
|
||||
(string) LocalizationUtility::translate('meta.title.listPostsByDate', 'ew_bloggy')
|
||||
);
|
||||
MetaTagService::set(MetaTagService::META_TITLE, (string) $title);
|
||||
MetaTagService::set(
|
||||
MetaTagService::META_DESCRIPTION,
|
||||
(string) LocalizationUtility::translate('meta.description.listPostsByDate', 'ew_bloggy')
|
||||
);
|
||||
}
|
||||
return $this->htmlResponse();
|
||||
}
|
||||
|
||||
protected function getPagination(QueryResultInterface $objects, int $currentPage = 1): ?BlogPagination
|
||||
{
|
||||
$maximumNumberOfLinks = (int) ($this->settings['lists']['pagination']['maximumNumberOfLinks'] ?? 0);
|
||||
$itemsPerPage = 10;
|
||||
if ($this->request->getFormat() === 'html') {
|
||||
$itemsPerPage = (int) ($this->settings['lists']['pagination']['itemsPerPage'] ?? 10);
|
||||
}
|
||||
|
||||
$paginator = new QueryResultPaginator($objects, $currentPage, $itemsPerPage);
|
||||
return new BlogPagination($paginator, $maximumNumberOfLinks);
|
||||
}
|
||||
}
|
||||
61
Classes/DataProcessing/BlogPostProcessor.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is developed by evoWeb.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Evoweb\EwBloggy\DataProcessing;
|
||||
|
||||
use Evoweb\EwBloggy\Domain\Repository\PostRepository;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
use TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface;
|
||||
|
||||
/**
|
||||
* Fetch blog records from the database, using the default .select syntax from TypoScript.
|
||||
*
|
||||
* This way, e.g. a FLUIDTEMPLATE cObject can iterate over the array of records.
|
||||
*
|
||||
* Example TypoScript configuration:
|
||||
*
|
||||
* 10 = Evoweb\EwBloggy\DataProcessing\BlogPostProcessor
|
||||
* 10 {
|
||||
* as = post
|
||||
* }
|
||||
*
|
||||
* 10 = blog-post
|
||||
*
|
||||
* where "as" means the variable to be containing the result-set from the DB query.
|
||||
*/
|
||||
readonly class BlogPostProcessor implements DataProcessorInterface
|
||||
{
|
||||
public function __construct(
|
||||
protected PostRepository $postRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
public function process(
|
||||
ContentObjectRenderer $cObj,
|
||||
array $contentObjectConfiguration,
|
||||
array $processorConfiguration,
|
||||
array $processedData
|
||||
): array {
|
||||
if (!$cObj->checkIf($processorConfiguration['if.'] ?? [])) {
|
||||
return $processedData;
|
||||
}
|
||||
|
||||
$targetVariableName = $cObj->stdWrapValue('as', $processorConfiguration, 'post');
|
||||
|
||||
$processedData[$targetVariableName] = $this->postRepository->findByUid((int)$processedData['data']['uid']);
|
||||
|
||||
return $processedData;
|
||||
}
|
||||
}
|
||||
175
Classes/Domain/Model/Author.php
Normal file
@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is developed by evoWeb.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Evoweb\EwBloggy\Domain\Model;
|
||||
|
||||
use TYPO3\CMS\Extbase\Annotation as Extbase;
|
||||
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
|
||||
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
|
||||
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
|
||||
|
||||
class Author extends AbstractEntity
|
||||
{
|
||||
protected string $name = '';
|
||||
|
||||
protected string $title = '';
|
||||
|
||||
protected ?FileReference $image = null;
|
||||
|
||||
protected string $email = '';
|
||||
|
||||
protected string $website = '';
|
||||
|
||||
protected string $profile = '';
|
||||
|
||||
protected string $bio = '';
|
||||
|
||||
protected int $detailsPage = 0;
|
||||
|
||||
/**
|
||||
* @var ObjectStorage<Post>
|
||||
*/
|
||||
#[Extbase\ORM\Lazy]
|
||||
protected ObjectStorage $posts;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->initializeObject();
|
||||
}
|
||||
|
||||
public function initializeObject(): void
|
||||
{
|
||||
$this->posts = new ObjectStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ObjectStorage<Post>
|
||||
*/
|
||||
public function getPosts(): ObjectStorage
|
||||
{
|
||||
return $this->posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ObjectStorage<Post> $posts
|
||||
*/
|
||||
public function setPosts(ObjectStorage $posts): void
|
||||
{
|
||||
$this->posts = $posts;
|
||||
}
|
||||
|
||||
public function addPost(Post $post): void
|
||||
{
|
||||
$this->posts->attach($post);
|
||||
}
|
||||
|
||||
public function removePost(Post $post): void
|
||||
{
|
||||
$this->posts->detach($post);
|
||||
}
|
||||
|
||||
public function getName(): ?string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setName(string $name): void
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setTitle(string $title): void
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getImage(): ?FileReference
|
||||
{
|
||||
return $this->image;
|
||||
}
|
||||
|
||||
public function setImage(FileReference $image): void
|
||||
{
|
||||
$this->image = $image;
|
||||
}
|
||||
|
||||
public function getWebsite(): string
|
||||
{
|
||||
return $this->website;
|
||||
}
|
||||
|
||||
public function setWebsite(string $website): void
|
||||
{
|
||||
$this->website = $website;
|
||||
}
|
||||
|
||||
public function getEmail(): string
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
public function setEmail(string $email): void
|
||||
{
|
||||
$this->email = $email;
|
||||
}
|
||||
|
||||
public function getProfile(): string
|
||||
{
|
||||
return $this->profile;
|
||||
}
|
||||
|
||||
public function setProfile(string $profile): void
|
||||
{
|
||||
$this->profile = $profile;
|
||||
}
|
||||
|
||||
public function getBio(): string
|
||||
{
|
||||
return $this->bio;
|
||||
}
|
||||
|
||||
public function setBio(string $bio): void
|
||||
{
|
||||
$this->bio = $bio;
|
||||
}
|
||||
|
||||
public function getDetailsPage(): int
|
||||
{
|
||||
return $this->detailsPage;
|
||||
}
|
||||
|
||||
public function setDetailsPage(int $page): void
|
||||
{
|
||||
$this->detailsPage = $page;
|
||||
}
|
||||
|
||||
public function getAllTags(): array
|
||||
{
|
||||
$uniqueTags = [];
|
||||
foreach ($this->getPosts() as $post) {
|
||||
foreach ($post->getTags() as $tag) {
|
||||
if (!array_key_exists((int) $tag->getUid(), $uniqueTags)) {
|
||||
$uniqueTags[(int) $tag->getUid()] = $tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $uniqueTags;
|
||||
}
|
||||
}
|
||||
102
Classes/Domain/Model/Category.php
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is developed by evoWeb.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Evoweb\EwBloggy\Domain\Model;
|
||||
|
||||
use Evoweb\EwBloggy\Constants;
|
||||
use TYPO3\CMS\Extbase\Annotation as Extbase;
|
||||
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
|
||||
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
|
||||
use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy;
|
||||
|
||||
class Category extends AbstractEntity
|
||||
{
|
||||
protected string $title = '';
|
||||
|
||||
protected string $description = '';
|
||||
|
||||
protected int $recordType = Constants::CATEGORY_TYPE_BLOG;
|
||||
|
||||
#[Extbase\ORM\Lazy]
|
||||
protected Category|LazyLoadingProxy|null $parent;
|
||||
|
||||
/**
|
||||
* @var ObjectStorage<Post>
|
||||
*/
|
||||
#[Extbase\ORM\Lazy]
|
||||
protected ObjectStorage $posts;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->initializeObject();
|
||||
}
|
||||
|
||||
public function initializeObject(): void
|
||||
{
|
||||
$this->posts = new ObjectStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ObjectStorage<Post>
|
||||
*/
|
||||
public function getPosts(): ObjectStorage
|
||||
{
|
||||
return $this->posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ObjectStorage<Post> $posts
|
||||
*/
|
||||
public function setPosts(ObjectStorage $posts): void
|
||||
{
|
||||
$this->posts = $posts;
|
||||
}
|
||||
|
||||
public function getRecordType(): int
|
||||
{
|
||||
return $this->recordType;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setTitle(string $title): void
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function setDescription(string $description): self
|
||||
{
|
||||
$this->description = $description;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getParent(): ?self
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
public function setParent(self $parent): void
|
||||
{
|
||||
$this->parent = $parent;
|
||||
}
|
||||
}
|
||||
324
Classes/Domain/Model/Post.php
Normal file
@ -0,0 +1,324 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is developed by evoWeb.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Evoweb\EwBloggy\Domain\Model;
|
||||
|
||||
use Evoweb\EwBloggy\Constants;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Annotation as Extbase;
|
||||
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
|
||||
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
|
||||
use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
|
||||
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
use TYPO3\CMS\Frontend\Typolink\LinkFactory;
|
||||
|
||||
class Post extends AbstractEntity
|
||||
{
|
||||
protected bool $hidden = false;
|
||||
|
||||
protected int $doktype = Constants::DOKTYPE_BLOG_POST;
|
||||
|
||||
protected string $title = '';
|
||||
|
||||
protected string $subtitle = '';
|
||||
|
||||
protected string $abstract = '';
|
||||
|
||||
protected string $description = '';
|
||||
|
||||
protected bool $commentsActive = true;
|
||||
|
||||
protected int $archiveDate = 0;
|
||||
|
||||
protected int $publishDate = 0;
|
||||
|
||||
protected \DateTime $crdate;
|
||||
|
||||
protected ?FileReference $featuredImage = null;
|
||||
|
||||
/**
|
||||
* @var ObjectStorage<Category>
|
||||
*/
|
||||
#[Extbase\ORM\Lazy]
|
||||
protected ObjectStorage $categories;
|
||||
|
||||
/**
|
||||
* @var ObjectStorage<Tag>
|
||||
*/
|
||||
#[Extbase\ORM\Lazy]
|
||||
protected ObjectStorage $tags;
|
||||
|
||||
/**
|
||||
* @var ObjectStorage<Author>
|
||||
*/
|
||||
#[Extbase\ORM\Lazy]
|
||||
protected ObjectStorage $authors;
|
||||
|
||||
/**
|
||||
* @var ObjectStorage<FileReference>
|
||||
*/
|
||||
#[Extbase\ORM\Lazy]
|
||||
protected ObjectStorage $media;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->initializeObject();
|
||||
}
|
||||
|
||||
public function initializeObject(): void
|
||||
{
|
||||
$this->categories = new ObjectStorage();
|
||||
$this->tags = new ObjectStorage();
|
||||
$this->authors = new ObjectStorage();
|
||||
$this->media = new ObjectStorage();
|
||||
}
|
||||
|
||||
public function addCategory(Category $category): void
|
||||
{
|
||||
$this->categories->attach($category);
|
||||
}
|
||||
|
||||
public function removeCategory(Category $category): void
|
||||
{
|
||||
$this->categories->detach($category);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ObjectStorage<Category>
|
||||
*/
|
||||
public function getCategories(): ObjectStorage
|
||||
{
|
||||
return $this->categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ObjectStorage<Category> $categories
|
||||
*/
|
||||
public function setCategories(ObjectStorage $categories): void
|
||||
{
|
||||
$this->categories = $categories;
|
||||
}
|
||||
|
||||
public function addTag(Tag $tag): void
|
||||
{
|
||||
$this->tags->attach($tag);
|
||||
}
|
||||
|
||||
public function removeTag(Tag $tag): void
|
||||
{
|
||||
$this->tags->detach($tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ObjectStorage<Tag>
|
||||
*/
|
||||
public function getTags(): ObjectStorage
|
||||
{
|
||||
return $this->tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ObjectStorage<Tag> $tags
|
||||
*/
|
||||
public function setTags(ObjectStorage $tags): void
|
||||
{
|
||||
$this->tags = $tags;
|
||||
}
|
||||
|
||||
public function addAuthor(Author $author): void
|
||||
{
|
||||
$this->authors->attach($author);
|
||||
}
|
||||
|
||||
public function removeAuthor(Author $author): void
|
||||
{
|
||||
$this->authors->detach($author);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ObjectStorage<Author>
|
||||
*/
|
||||
public function getAuthors(): ObjectStorage
|
||||
{
|
||||
return $this->authors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ObjectStorage<Author> $authors
|
||||
*/
|
||||
public function setAuthors(ObjectStorage $authors): void
|
||||
{
|
||||
$this->authors = $authors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ObjectStorage<FileReference>
|
||||
*/
|
||||
public function getMedia(): ObjectStorage
|
||||
{
|
||||
return $this->media;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ObjectStorage<FileReference> $media
|
||||
*/
|
||||
public function setMedia(ObjectStorage $media): void
|
||||
{
|
||||
$this->media = $media;
|
||||
}
|
||||
|
||||
public function getFeaturedImage(): ?FileReference
|
||||
{
|
||||
return $this->featuredImage;
|
||||
}
|
||||
|
||||
public function setFeaturedImage(?FileReference $featuredImage): void
|
||||
{
|
||||
$this->featuredImage = $featuredImage;
|
||||
}
|
||||
|
||||
public function getHidden(): bool
|
||||
{
|
||||
return $this->hidden;
|
||||
}
|
||||
|
||||
public function isHidden(): bool
|
||||
{
|
||||
return $this->getHidden();
|
||||
}
|
||||
|
||||
public function getDoktype(): int
|
||||
{
|
||||
return $this->doktype;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setTitle(string $title): void
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getSubtitle(): string
|
||||
{
|
||||
return $this->subtitle;
|
||||
}
|
||||
|
||||
public function setSubtitle(string $subtitle): void
|
||||
{
|
||||
$this->subtitle = $subtitle;
|
||||
}
|
||||
|
||||
public function getAbstract(): string
|
||||
{
|
||||
return $this->abstract;
|
||||
}
|
||||
|
||||
public function setAbstract(string $abstract): void
|
||||
{
|
||||
$this->abstract = $abstract;
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function setDescription(string $description): void
|
||||
{
|
||||
$this->description = $description;
|
||||
}
|
||||
|
||||
public function getCrdate(): \DateTime
|
||||
{
|
||||
return $this->crdate;
|
||||
}
|
||||
|
||||
public function setCrdate(\DateTime $crdate): void
|
||||
{
|
||||
$this->crdate = $crdate;
|
||||
}
|
||||
|
||||
public function getCommentsActive(): bool
|
||||
{
|
||||
return $this->commentsActive;
|
||||
}
|
||||
|
||||
public function setCommentsActive(bool $commentsActive): void
|
||||
{
|
||||
$this->commentsActive = $commentsActive;
|
||||
}
|
||||
|
||||
public function getArchiveDate(): int
|
||||
{
|
||||
return $this->archiveDate;
|
||||
}
|
||||
|
||||
public function setArchiveDate(int $archiveDate): void
|
||||
{
|
||||
$this->archiveDate = $archiveDate;
|
||||
}
|
||||
|
||||
public function getPublishDate(): int
|
||||
{
|
||||
return $this->publishDate;
|
||||
}
|
||||
|
||||
public function setPublishDate(int $publishDate): void
|
||||
{
|
||||
$this->publishDate = $publishDate;
|
||||
}
|
||||
|
||||
public function getUri(): string
|
||||
{
|
||||
if (class_exists(LinkFactory::class)) {
|
||||
return (string) GeneralUtility::makeInstance(LinkFactory::class)->create(
|
||||
'',
|
||||
[
|
||||
'parameter' => (string) $this->getUid(),
|
||||
'forceAbsoluteUrl' => true
|
||||
],
|
||||
GeneralUtility::makeInstance(ContentObjectRenderer::class)
|
||||
)->getUrl();
|
||||
}
|
||||
|
||||
return GeneralUtility::makeInstance(UriBuilder::class)
|
||||
->setCreateAbsoluteUri(true)
|
||||
->setTargetPageUid((int) $this->getUid())
|
||||
->build();
|
||||
}
|
||||
|
||||
public function getAsArray(): array
|
||||
{
|
||||
return $this->__toArray();
|
||||
}
|
||||
|
||||
public function __toArray(): array
|
||||
{
|
||||
return [
|
||||
'uid' => $this->getUid(),
|
||||
'hidden' => $this->getHidden(),
|
||||
'doktype' => $this->getDoktype(),
|
||||
'title' => $this->getTitle(),
|
||||
'subtitle' => $this->getSubtitle(),
|
||||
'abstract' => $this->getAbstract(),
|
||||
'description' => $this->getDescription()
|
||||
];
|
||||
}
|
||||
}
|
||||
45
Classes/Domain/Model/Tag.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is developed by evoWeb.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Evoweb\EwBloggy\Domain\Model;
|
||||
|
||||
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
|
||||
|
||||
class Tag extends AbstractEntity
|
||||
{
|
||||
protected string $title = '';
|
||||
|
||||
protected string $description = '';
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setTitle(string $title): void
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function setDescription(string $description): void
|
||||
{
|
||||
$this->description = $description;
|
||||
}
|
||||
}
|
||||
166
Classes/Domain/Repository/PostRepository.php
Normal file
@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is developed by evoWeb.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Evoweb\EwBloggy\Domain\Repository;
|
||||
|
||||
use DateMalformedStringException;
|
||||
use DateTimeImmutable;
|
||||
use Evoweb\EwBloggy\Constants;
|
||||
use Evoweb\EwBloggy\Domain\Model\Post;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException;
|
||||
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
|
||||
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
|
||||
use TYPO3\CMS\Extbase\Persistence\Repository;
|
||||
|
||||
/**
|
||||
* A post repository
|
||||
*
|
||||
* @extends Repository<Post>
|
||||
*/
|
||||
class PostRepository extends Repository
|
||||
{
|
||||
protected array $defaultConstraints = [];
|
||||
|
||||
public function initializeObject(): void
|
||||
{
|
||||
$query = $this->createQuery();
|
||||
$this->defaultConstraints[] = $query->equals('doktype', Constants::DOKTYPE_BLOG_POST);
|
||||
if ($this->getRequest()->getAttribute('language')?->getLanguageId() === 0) {
|
||||
$this->defaultConstraints[] = $query->logicalOr(
|
||||
$query->equals('l18n_cfg', 0),
|
||||
$query->equals('l18n_cfg', 2)
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
$this->defaultConstraints[] = $query->lessThan('l18n_cfg', 2);
|
||||
} catch (InvalidQueryException) {
|
||||
}
|
||||
}
|
||||
|
||||
$this->defaultOrderings = [
|
||||
'publish_date' => QueryInterface::ORDER_DESCENDING,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QueryResultInterface<Post>
|
||||
*/
|
||||
public function findAll(): QueryResultInterface
|
||||
{
|
||||
return $this->getFindAllQuery()->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QueryResultInterface<Post>
|
||||
*/
|
||||
public function findAllWithLimit(int $limit): QueryResultInterface
|
||||
{
|
||||
$query = $this->getFindAllQuery();
|
||||
$query->setLimit($limit);
|
||||
|
||||
return $query->execute();
|
||||
}
|
||||
|
||||
public function findMonthsAndYearsWithPosts(): array
|
||||
{
|
||||
$query = $this->createQuery();
|
||||
$constraints = $this->defaultConstraints;
|
||||
|
||||
try {
|
||||
$constraints[] = $query->greaterThan('crdateMonth', 0);
|
||||
$constraints[] = $query->greaterThan('crdateYear', 0);
|
||||
} catch (InvalidQueryException) {
|
||||
}
|
||||
|
||||
$query->matching($query->logicalAnd(...$constraints));
|
||||
$posts = $query->execute(true);
|
||||
|
||||
$result = [];
|
||||
$currentIndex = -1;
|
||||
$currentYear = null;
|
||||
$currentMonth = null;
|
||||
foreach ($posts as $post) {
|
||||
$year = $post['crdate_year'];
|
||||
$month = $post['crdate_month'];
|
||||
if ($currentYear !== $year || $currentMonth !== $month) {
|
||||
$currentIndex++;
|
||||
$currentYear = $year;
|
||||
$currentMonth = $month;
|
||||
$result[$currentIndex] = [
|
||||
'year' => $currentYear,
|
||||
'month' => $currentMonth,
|
||||
'count' => 1
|
||||
];
|
||||
} else {
|
||||
$result[$currentIndex]['count']++;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QueryResultInterface<Post>
|
||||
*/
|
||||
public function findByMonthAndYear(int $year, ?int $month = null): QueryResultInterface
|
||||
{
|
||||
$query = $this->createQuery();
|
||||
$constraints = $this->defaultConstraints;
|
||||
|
||||
try {
|
||||
if ($month !== null) {
|
||||
$startDate = new DateTimeImmutable(sprintf('%d-%d-1 00:00:00', $year, $month));
|
||||
$endDate = new DateTimeImmutable(
|
||||
sprintf('%d-%d-%d 23:59:59', $year, $month, (int)$startDate->format('t'))
|
||||
);
|
||||
} else {
|
||||
$startDate = new DateTimeImmutable(sprintf('%d-1-1 00:00:00', $year));
|
||||
$endDate = new DateTimeImmutable(sprintf('%d-12-31 23:59:59', $year));
|
||||
}
|
||||
$constraints[] = $query->greaterThanOrEqual('publish_date', $startDate->getTimestamp());
|
||||
$constraints[] = $query->lessThanOrEqual('publish_date', $endDate->getTimestamp());
|
||||
} catch (InvalidQueryException | DateMalformedStringException) {
|
||||
}
|
||||
|
||||
return $query->matching($query->logicalAnd(...$constraints))->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QueryInterface<Post>
|
||||
*/
|
||||
protected function getFindAllQuery(): QueryInterface
|
||||
{
|
||||
$query = $this->createQuery();
|
||||
$constraints = $this->defaultConstraints;
|
||||
|
||||
try {
|
||||
$constraints[] = $query->logicalOr(
|
||||
$query->equals('archiveDate', 0),
|
||||
$query->greaterThanOrEqual('archiveDate', time())
|
||||
);
|
||||
} catch (InvalidQueryException) {
|
||||
}
|
||||
|
||||
$query->matching($query->logicalAnd(...$constraints));
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function getRequest(): ?ServerRequestInterface
|
||||
{
|
||||
return $GLOBALS['TYPO3_REQUEST'];
|
||||
}
|
||||
}
|
||||
34
Classes/EventListener/ModifyPageLayoutContent.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is developed by evoWeb.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Evoweb\EwBloggy\EventListener;
|
||||
|
||||
use Evoweb\EwBloggy\Backend\View\PostHeaderRenderer;
|
||||
use TYPO3\CMS\Backend\Controller\Event\ModifyPageLayoutContentEvent;
|
||||
|
||||
readonly class ModifyPageLayoutContent
|
||||
{
|
||||
public function __construct(
|
||||
private PostHeaderRenderer $blogPostHeaderContentRenderer
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(ModifyPageLayoutContentEvent $event): void
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
$content = $this->blogPostHeaderContentRenderer->render($request);
|
||||
$event->addHeaderContent($content);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is developed by evoWeb.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Evoweb\EwBloggy\EventListener;
|
||||
|
||||
use Evoweb\EwBloggy\Backend\View\PostHeaderRenderer;
|
||||
use TYPO3\CMS\Backend\Controller\Event\RenderAdditionalContentToRecordListEvent;
|
||||
|
||||
readonly class RenderAdditionalContentToRecordList
|
||||
{
|
||||
public function __construct(
|
||||
private PostHeaderRenderer $blogPostHeaderContentRenderer
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(RenderAdditionalContentToRecordListEvent $event): void
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
$content = $this->blogPostHeaderContentRenderer->render($request);
|
||||
$event->addContentAbove($content);
|
||||
}
|
||||
}
|
||||
167
Classes/Pagination/BlogPagination.php
Normal file
@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is developed by evoWeb.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Evoweb\EwBloggy\Pagination;
|
||||
|
||||
use TYPO3\CMS\Core\Pagination\PaginationInterface;
|
||||
use TYPO3\CMS\Core\Pagination\PaginatorInterface;
|
||||
|
||||
final class BlogPagination implements PaginationInterface
|
||||
{
|
||||
protected int $maximumNumberOfLinks = 10;
|
||||
protected int $displayRangeStart = 0;
|
||||
protected int $displayRangeEnd = 0;
|
||||
protected PaginatorInterface $paginator;
|
||||
|
||||
public function __construct(PaginatorInterface $paginator, int $maximumNumberOfLinks = 10)
|
||||
{
|
||||
$this->paginator = $paginator;
|
||||
$this->maximumNumberOfLinks = $maximumNumberOfLinks > 0 ? $maximumNumberOfLinks : 1;
|
||||
$this->displayRangeStart = $this->getFirstPageNumber();
|
||||
$this->displayRangeEnd = $this->getLastPageNumber();
|
||||
|
||||
$this->generateDisplayRange();
|
||||
}
|
||||
|
||||
protected function generateDisplayRange(): void
|
||||
{
|
||||
$currentPageNumber = $this->getCurrentPageNumber();
|
||||
$maximumNumberOfLinks = $this->getMaximumNumberOfLinks();
|
||||
$delta = (int) floor($maximumNumberOfLinks / 2);
|
||||
if ($this->getNumberOfPages() > $maximumNumberOfLinks) {
|
||||
$this->displayRangeStart = $currentPageNumber - $delta;
|
||||
$this->displayRangeEnd = $this->displayRangeStart + $maximumNumberOfLinks - 1;
|
||||
while ($this->displayRangeStart < $this->getFirstPageNumber()) {
|
||||
$this->displayRangeStart++;
|
||||
$this->displayRangeEnd++;
|
||||
}
|
||||
while ($this->displayRangeEnd > $this->getLastPageNumber()) {
|
||||
$this->displayRangeStart--;
|
||||
$this->displayRangeEnd--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getPreviousPageNumber(): ?int
|
||||
{
|
||||
$previousPage = $this->paginator->getCurrentPageNumber() - 1;
|
||||
|
||||
if ($previousPage > $this->paginator->getNumberOfPages()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $previousPage >= $this->getFirstPageNumber()
|
||||
? $previousPage
|
||||
: null;
|
||||
}
|
||||
|
||||
public function getCurrentPageNumber(): int
|
||||
{
|
||||
return $this->paginator->getCurrentPageNumber();
|
||||
}
|
||||
|
||||
public function getNextPageNumber(): ?int
|
||||
{
|
||||
$nextPage = $this->paginator->getCurrentPageNumber() + 1;
|
||||
|
||||
return $nextPage <= $this->paginator->getNumberOfPages()
|
||||
? $nextPage
|
||||
: null;
|
||||
}
|
||||
|
||||
public function getFirstPageNumber(): int
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
public function getLastPageNumber(): int
|
||||
{
|
||||
return $this->paginator->getNumberOfPages();
|
||||
}
|
||||
|
||||
public function getStartRecordNumber(): int
|
||||
{
|
||||
if ($this->paginator->getCurrentPageNumber() > $this->paginator->getNumberOfPages()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $this->paginator->getKeyOfFirstPaginatedItem() + 1;
|
||||
}
|
||||
|
||||
public function getEndRecordNumber(): int
|
||||
{
|
||||
if ($this->paginator->getCurrentPageNumber() > $this->paginator->getNumberOfPages()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $this->paginator->getKeyOfLastPaginatedItem() + 1;
|
||||
}
|
||||
|
||||
public function getNumberOfPages(): int
|
||||
{
|
||||
return $this->paginator->getNumberOfPages();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
public function getAllPageNumbers(): array
|
||||
{
|
||||
return range($this->getFirstPageNumber(), $this->getLastPageNumber());
|
||||
}
|
||||
|
||||
public function getDisplayRangeStart(): int
|
||||
{
|
||||
return $this->displayRangeStart;
|
||||
}
|
||||
|
||||
public function getDisplayRangeEnd(): int
|
||||
{
|
||||
return $this->displayRangeEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
public function getDisplayPageNumbers(): array
|
||||
{
|
||||
return range($this->getDisplayRangeStart(), $this->getDisplayRangeEnd());
|
||||
}
|
||||
|
||||
public function hasLessPages(): bool
|
||||
{
|
||||
return $this->getDisplayRangeStart() > $this->getFirstPageNumber();
|
||||
}
|
||||
|
||||
public function hasMorePages(): bool
|
||||
{
|
||||
return $this->getDisplayRangeEnd() < $this->getLastPageNumber();
|
||||
}
|
||||
|
||||
public function getMaximumNumberOfLinks(): int
|
||||
{
|
||||
return $this->maximumNumberOfLinks;
|
||||
}
|
||||
|
||||
public function getPaginator(): PaginatorInterface
|
||||
{
|
||||
return $this->paginator;
|
||||
}
|
||||
|
||||
public function getPaginatedItems(): iterable
|
||||
{
|
||||
return $this->paginator->getPaginatedItems();
|
||||
}
|
||||
}
|
||||
76
Classes/ViewHelpers/Link/ArchiveViewHelper.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is developed by evoWeb.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Evoweb\EwBloggy\ViewHelpers\Link;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
|
||||
|
||||
class ArchiveViewHelper extends AbstractTagBasedViewHelper
|
||||
{
|
||||
protected $tagName = 'a';
|
||||
|
||||
public function __construct(protected UriBuilder $uriBuilder)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function initializeArguments(): void
|
||||
{
|
||||
parent::initializeArguments();
|
||||
|
||||
$this->registerArgument('month', 'int', 'The month to link to');
|
||||
$this->registerArgument('year', 'int', 'The year to link to', true);
|
||||
}
|
||||
|
||||
public function render(): string
|
||||
{
|
||||
$request = $this->renderingContext->getAttribute(ServerRequestInterface::class);
|
||||
|
||||
$year = (int)$this->arguments['year'];
|
||||
$month = (int)$this->arguments['month'];
|
||||
// @todo migrate to site settings
|
||||
$pageUid = (int)($request->getAttribute('frontend.typoscript')
|
||||
->getSetupTree()
|
||||
->getChildByName('plugin')
|
||||
?->getChildByName('tx_ewbloggy')
|
||||
?->getChildByName('settings')
|
||||
?->getChildByName('archiveUid')
|
||||
?->getValue() ?? 0);
|
||||
|
||||
$arguments = [
|
||||
'year' => $year
|
||||
];
|
||||
if ($month > 0) {
|
||||
$arguments['month'] = $month;
|
||||
}
|
||||
$this->uriBuilder->reset()
|
||||
->setRequest($request)
|
||||
->setTargetPageUid($pageUid);
|
||||
$uri = $this->uriBuilder->uriFor('listPostsByDate', $arguments, 'Post', 'EwBloggy', 'Archive');
|
||||
$linkText = $this->renderChildren() ?? implode('-', $arguments);
|
||||
|
||||
if ($uri !== '') {
|
||||
$this->tag->addAttribute('href', $uri);
|
||||
$this->tag->setContent((string)$linkText);
|
||||
$result = $this->tag->render();
|
||||
} else {
|
||||
$result = $linkText;
|
||||
}
|
||||
|
||||
return (string)$result;
|
||||
}
|
||||
}
|
||||
87
Classes/ViewHelpers/Link/Be/PostViewHelper.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is developed by evoWeb.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Evoweb\EwBloggy\ViewHelpers\Link\Be;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Evoweb\EwBloggy\Domain\Model\Post;
|
||||
use TYPO3\CMS\Backend\Routing\UriBuilder;
|
||||
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
|
||||
|
||||
class PostViewHelper extends AbstractTagBasedViewHelper
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $tagName = 'a';
|
||||
|
||||
public function __construct(readonly private UriBuilder $uriBuilder)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function initializeArguments(): void
|
||||
{
|
||||
parent::initializeArguments();
|
||||
|
||||
$this->registerArgument('post', Post::class, 'The post to link to', true);
|
||||
$this->registerArgument('returnUri', 'bool', 'return only uri', false, false);
|
||||
$this->registerArgument('action', 'string', 'action to link', false, null);
|
||||
}
|
||||
|
||||
public function render(): string
|
||||
{
|
||||
$request = $this->renderingContext->getAttribute(ServerRequestInterface::class);
|
||||
if (!$request instanceof ServerRequestInterface) {
|
||||
throw new \RuntimeException(
|
||||
'ViewHelper ew:link.be.post needs a request implementing ServerRequestInterface.',
|
||||
1684305293
|
||||
);
|
||||
}
|
||||
|
||||
/** @var Post $post */
|
||||
$post = $this->arguments['post'];
|
||||
|
||||
switch ($this->arguments['action']) {
|
||||
case 'edit':
|
||||
$uri = (string)$this->uriBuilder->buildUriFromRoute('record_edit', [
|
||||
'edit' => ['pages' => [$post->getUid() => 'edit']],
|
||||
'returnUrl' => $request->getAttribute('normalizedParams')->getRequestUri(),
|
||||
]);
|
||||
break;
|
||||
default:
|
||||
$uri = (string)$this->uriBuilder->buildUriFromRoute('web_layout', [
|
||||
'id' => $post->getUid(),
|
||||
'returnUrl' => $request->getAttribute('normalizedParams')->getRequestUri(),
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
||||
if (isset($this->arguments['returnUri']) && $this->arguments['returnUri'] === true) {
|
||||
return htmlspecialchars($uri, ENT_QUOTES | ENT_HTML5);
|
||||
}
|
||||
|
||||
$linkText = $this->renderChildren() ?? (
|
||||
$post->getTitle() !== ''
|
||||
? $post->getTitle()
|
||||
: LocalizationUtility::translate('backend.message.nopost', 'blog')
|
||||
);
|
||||
$this->tag->addAttribute('href', $uri);
|
||||
$this->tag->setContent($linkText);
|
||||
|
||||
return $this->tag->render();
|
||||
}
|
||||
}
|
||||
18
Configuration/Extbase/Persistence/Classes.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
/*
|
||||
* This file is part of the package t3g/blog.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
return [
|
||||
\Evoweb\EwBloggy\Domain\Model\Post::class => [
|
||||
'tableName' => 'pages',
|
||||
],
|
||||
\Evoweb\EwBloggy\Domain\Model\Category::class => [
|
||||
'tableName' => 'sys_category',
|
||||
],
|
||||
];
|
||||
28
Configuration/FlexForms/archive.xml
Normal file
@ -0,0 +1,28 @@
|
||||
<T3DataStructure>
|
||||
<sheets>
|
||||
<sDEF>
|
||||
<ROOT>
|
||||
<sheetTitle>LLL:EXT:ew_bloggy/Resources/Private/Language/locallang_db.xlf:archive</sheetTitle>
|
||||
<type>array</type>
|
||||
<el>
|
||||
<settings.flexform.limit>
|
||||
<label>LLL:EXT:ew_bloggy/Resources/Private/Language/locallang_db.xlf:settings.flexform.limit</label>
|
||||
<config>
|
||||
<type>number</type>
|
||||
</config>
|
||||
</settings.flexform.limit>
|
||||
|
||||
<settings.flexform.postsUid>
|
||||
<label>LLL:EXT:ew_bloggy/Resources/Private/Language/locallang_db.xlf:settings.flexform.postsUid</label>
|
||||
<config>
|
||||
<type>group</type>
|
||||
<allowed>pages</allowed>
|
||||
<size>1</size>
|
||||
<maxitems>1</maxitems>
|
||||
</config>
|
||||
</settings.flexform.postsUid>
|
||||
</el>
|
||||
</ROOT>
|
||||
</sDEF>
|
||||
</sheets>
|
||||
</T3DataStructure>
|
||||
38
Configuration/FlexForms/latestposts.xml
Normal file
@ -0,0 +1,38 @@
|
||||
<T3DataStructure>
|
||||
<sheets>
|
||||
<sDEF>
|
||||
<ROOT>
|
||||
<sheetTitle>LLL:EXT:ew_bloggy/Resources/Private/Language/locallang_db.xlf:latestpost</sheetTitle>
|
||||
<type>array</type>
|
||||
<el>
|
||||
<settings.flexform.limit>
|
||||
<label>LLL:EXT:ew_bloggy/Resources/Private/Language/locallang_db.xlf:settings.flexform.limit</label>
|
||||
<config>
|
||||
<type>number</type>
|
||||
</config>
|
||||
</settings.flexform.limit>
|
||||
|
||||
<settings.flexform.postsUid>
|
||||
<label>LLL:EXT:ew_bloggy/Resources/Private/Language/locallang_db.xlf:settings.flexform.postsUid</label>
|
||||
<config>
|
||||
<type>group</type>
|
||||
<allowed>pages</allowed>
|
||||
<size>1</size>
|
||||
<maxitems>1</maxitems>
|
||||
</config>
|
||||
</settings.flexform.postsUid>
|
||||
|
||||
<settings.flexform.archiveUid>
|
||||
<label>LLL:EXT:ew_bloggy/Resources/Private/Language/locallang_db.xlf:settings.flexform.postsUid</label>
|
||||
<config>
|
||||
<type>group</type>
|
||||
<allowed>pages</allowed>
|
||||
<size>1</size>
|
||||
<maxitems>1</maxitems>
|
||||
</config>
|
||||
</settings.flexform.archiveUid>
|
||||
</el>
|
||||
</ROOT>
|
||||
</sDEF>
|
||||
</sheets>
|
||||
</T3DataStructure>
|
||||
28
Configuration/FlexForms/posts.xml
Normal file
@ -0,0 +1,28 @@
|
||||
<T3DataStructure>
|
||||
<sheets>
|
||||
<sDEF>
|
||||
<ROOT>
|
||||
<sheetTitle>LLL:EXT:ew_bloggy/Resources/Private/Language/locallang_db.xlf:posts</sheetTitle>
|
||||
<type>array</type>
|
||||
<el>
|
||||
<settings.flexform.limit>
|
||||
<label>LLL:EXT:ew_bloggy/Resources/Private/Language/locallang_db.xlf:settings.flexform.limit</label>
|
||||
<config>
|
||||
<type>number</type>
|
||||
</config>
|
||||
</settings.flexform.limit>
|
||||
|
||||
<settings.flexform.archiveUid>
|
||||
<label>LLL:EXT:ew_bloggy/Resources/Private/Language/locallang_db.xlf:settings.flexform.postsUid</label>
|
||||
<config>
|
||||
<type>group</type>
|
||||
<allowed>pages</allowed>
|
||||
<size>1</size>
|
||||
<maxitems>1</maxitems>
|
||||
</config>
|
||||
</settings.flexform.archiveUid>
|
||||
</el>
|
||||
</ROOT>
|
||||
</sDEF>
|
||||
</sheets>
|
||||
</T3DataStructure>
|
||||
12
Configuration/Icons.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
use TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider;
|
||||
|
||||
return array_map(static fn (string $source) => ['provider' => SvgIconProvider::class, 'source' => $source], [
|
||||
'record-blog-author' => 'EXT:ew_bloggy/Resources/Public/Icons/blog-author.svg',
|
||||
'record-blog-category' => 'EXT:ew_bloggy/Resources/Public/Icons/blog-category.svg',
|
||||
'record-blog-post' => 'EXT:ew_bloggy/Resources/Public/Icons/blog-post.svg',
|
||||
'plugin-blog-archive' => 'EXT:ew_bloggy/Resources/Public/Icons/plugin-blog-archive.svg',
|
||||
'plugin-blog-latestposts' => 'EXT:ew_bloggy/Resources/Public/Icons/plugin-blog-latestposts.svg',
|
||||
'plugin-blog-posts' => 'EXT:ew_bloggy/Resources/Public/Icons/plugin-blog-posts.svg',
|
||||
]);
|
||||
23
Configuration/Services.yaml
Normal file
@ -0,0 +1,23 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
public: false
|
||||
|
||||
Evoweb\EwBloggy\:
|
||||
resource: '../Classes/*'
|
||||
|
||||
Evoweb\EwBloggy\DataProcessing\BlogPostProcessor:
|
||||
public: true
|
||||
tags:
|
||||
- { name: 'data.processor', identifier: 'blog-post' }
|
||||
|
||||
Evoweb\EwBloggy\EventListener\ModifyPageLayoutContent:
|
||||
tags:
|
||||
- name: event.listener
|
||||
identifier: 'evoweb/ew-bloggy/modify-page-module-content'
|
||||
|
||||
Evoweb\EwBloggy\EventListener\RenderAdditionalContentToRecordList:
|
||||
tags:
|
||||
- name: event.listener
|
||||
identifier: 'evoweb/ew-bloggy/render-additional-content-to-record-list'
|
||||
1
Configuration/Sets/Plugin/config.yaml
Normal file
@ -0,0 +1 @@
|
||||
name: evoweb/ew-bloggy
|
||||
24
Configuration/Sets/Plugin/labels.xlf
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
||||
<file source-language="en" datatype="plaintext" original="EXT:ew_bloggy/Configuration/Sets/Plugin/labels.xlf" date="2025-01-19T16:00:00Z" product-name="ew_bloggy">
|
||||
<header/>
|
||||
<body>
|
||||
<trans-unit id="label" resname="label">
|
||||
<source>Simple and small blog</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="categories.ew-bloggy" resname="categories.ew-bloggy">
|
||||
<source>Simple and small blog</source>
|
||||
</trans-unit>
|
||||
|
||||
<trans-unit id="settings.evoweb.tx-ewbloggy.limit" resname="settings.evoweb.tx-ewbloggy.limit">
|
||||
<source>Limit of posts to display</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="settings.evoweb.tx-ewbloggy.postsUid" resname="settings.evoweb.tx-ewbloggy.postsUid">
|
||||
<source>Posts page uid</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="settings.evoweb.tx-ewbloggy.archiveUid" resname="settings.evoweb.tx-ewbloggy.archiveUid">
|
||||
<source>Archive page uid</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
16
Configuration/Sets/Plugin/settings.definitions.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
categories:
|
||||
ew-bloggy: ~
|
||||
|
||||
settings:
|
||||
evoweb.tx-ewbloggy.limit:
|
||||
type: int
|
||||
default: 0
|
||||
category: ew-bloggy
|
||||
evoweb.tx-ewbloggy.postsUid:
|
||||
type: int
|
||||
default: 0
|
||||
category: ew-bloggy
|
||||
evoweb.tx-ewbloggy.archiveUid:
|
||||
type: int
|
||||
default: 0
|
||||
category: ew-bloggy
|
||||
1
Configuration/Sets/Plugin/setup.typoscript
Normal file
@ -0,0 +1 @@
|
||||
@import 'EXT:ew_bloggy/Configuration/TypoScript/setup.typoscript'
|
||||
206
Configuration/TCA/Overrides/pages-blog.php
Normal file
@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
// Add new page types as possible select item:
|
||||
use Evoweb\EwBloggy\Constants;
|
||||
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
|
||||
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||
|
||||
ExtensionManagementUtility::addTcaSelectItem(
|
||||
'pages',
|
||||
'doktype',
|
||||
[
|
||||
'label' => 'LLL:EXT:ew_bloggy/Resources/Private/Language/locallang_tca.xlf:pages.doktype.blog-post',
|
||||
'value' => (string) Constants::DOKTYPE_BLOG_POST,
|
||||
'icon' => 'record-blog-post',
|
||||
],
|
||||
'1',
|
||||
'after'
|
||||
);
|
||||
|
||||
// Add icon for new page types:
|
||||
$GLOBALS['TCA']['pages']['ctrl']['typeicon_classes'][(string) Constants::DOKTYPE_BLOG_POST] = 'record-blog-post';
|
||||
$GLOBALS['TCA']['pages']['types'][(string) Constants::DOKTYPE_BLOG_POST]
|
||||
= $GLOBALS['TCA']['pages']['types'][PageRepository::DOKTYPE_DEFAULT];
|
||||
|
||||
$languageFile = 'LLL:EXT:ew_bloggy/Resources/Private/Language/locallang_db.xlf:';
|
||||
|
||||
// Register fields
|
||||
$GLOBALS['TCA']['pages']['columns'] = array_replace_recursive(
|
||||
$GLOBALS['TCA']['pages']['columns'],
|
||||
[
|
||||
'crdate' => [
|
||||
'label' => 'crdate',
|
||||
'config' => [
|
||||
'type' => 'passthrough',
|
||||
],
|
||||
],
|
||||
'archive_date' => [
|
||||
'label' => $languageFile . 'pages.archive_date',
|
||||
'config' => [
|
||||
'type' => 'datetime',
|
||||
'size' => '13',
|
||||
'default' => '0',
|
||||
'behaviour' => [
|
||||
'allowLanguageSynchronization' => true
|
||||
]
|
||||
],
|
||||
],
|
||||
'publish_date' => [
|
||||
'label' => $languageFile . 'pages.publish_date',
|
||||
'config' => [
|
||||
'type' => 'datetime',
|
||||
'size' => '13',
|
||||
'default' => '0',
|
||||
'behaviour' => [
|
||||
'allowLanguageSynchronization' => true
|
||||
]
|
||||
],
|
||||
],
|
||||
'featured_image' => [
|
||||
'label' => $languageFile . 'pages.featured_image',
|
||||
'config' => [
|
||||
'type' => 'file',
|
||||
'minitems' => 0,
|
||||
'maxitems' => 1,
|
||||
'allowed' => 'common-image-types',
|
||||
'behaviour' => [
|
||||
'allowLanguageSynchronization' => true
|
||||
]
|
||||
],
|
||||
],
|
||||
'authors' => [
|
||||
'label' => $languageFile . 'pages.authors',
|
||||
'l10n_mode' => 'exclude',
|
||||
'config' => [
|
||||
'type' => 'select',
|
||||
'renderType' => 'selectMultipleSideBySide',
|
||||
'multiple' => false,
|
||||
'foreign_table' => 'tx_ewbloggy_domain_model_author',
|
||||
'foreign_table_where' => 'AND {#tx_ewbloggy_domain_model_author}.{#sys_language_uid} IN (0,-1)'
|
||||
. ' AND {#tx_ewbloggy_domain_model_author}.{#pid} = ###PAGE_TSCONFIG_ID###'
|
||||
. ' ORDER BY tx_ewbloggy_domain_model_author.name ASC',
|
||||
'MM' => 'tx_ewbloggy_post_author_mm',
|
||||
'minitems' => 0,
|
||||
'maxitems' => 99999
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
ExtensionManagementUtility::addFieldsToPalette(
|
||||
'pages',
|
||||
'publish_date',
|
||||
'publish_date, archive_date'
|
||||
);
|
||||
|
||||
ExtensionManagementUtility::addToAllTCAtypes(
|
||||
'pages',
|
||||
'
|
||||
--div--;' . $languageFile . 'pages.tabs.blog,
|
||||
--palette--;' . $languageFile . 'pages.palettes.publish_date;publish_date,
|
||||
featured_image,
|
||||
tags,
|
||||
authors,
|
||||
comments_active,
|
||||
comments
|
||||
',
|
||||
(string) Constants::DOKTYPE_BLOG_POST,
|
||||
'after:subtitle'
|
||||
);
|
||||
|
||||
$GLOBALS['TCA']['pages']['types'][Constants::DOKTYPE_BLOG_POST]['columnsOverrides'] = [
|
||||
'categories' => [
|
||||
'config' => [
|
||||
'behaviour' => [
|
||||
'allowLanguageSynchronization' => true
|
||||
],
|
||||
'foreign_table_where' => 'AND sys_category.sys_language_uid IN (0,-1)'
|
||||
. ' AND sys_category.pid = ###PAGE_TSCONFIG_ID###',
|
||||
]
|
||||
],
|
||||
'featured_image' => [
|
||||
'config' => [
|
||||
'maxitems' => 10,
|
||||
'overrideChildTca' => [
|
||||
'columns' => [
|
||||
'description' => [
|
||||
'type' => 'input',
|
||||
],
|
||||
'crop' => [
|
||||
'config' => [
|
||||
'cropVariants' => [
|
||||
'default' => [
|
||||
'disabled' => true,
|
||||
],
|
||||
'latest' => [
|
||||
'title' => 'Blog Latest',
|
||||
'allowedAspectRatios' => [
|
||||
'910:1080' => [
|
||||
'title' => '910:1080',
|
||||
'value' => 910 / 1080,
|
||||
],
|
||||
],
|
||||
'selectedRatio' => '910:1080',
|
||||
'cropArea' => [
|
||||
'x' => 0.0,
|
||||
'y' => 0.0,
|
||||
'width' => 1.0,
|
||||
'height' => 1.0,
|
||||
],
|
||||
],
|
||||
'list' => [
|
||||
'title' => 'Blog Archive',
|
||||
'allowedAspectRatios' => [
|
||||
'1:1' => [
|
||||
'title' => '1:1',
|
||||
'value' => 1,
|
||||
],
|
||||
],
|
||||
'selectedRatio' => '1:1',
|
||||
'cropArea' => [
|
||||
'x' => 0.0,
|
||||
'y' => 0.0,
|
||||
'width' => 1.0,
|
||||
'height' => 1.0,
|
||||
],
|
||||
],
|
||||
'details' => [
|
||||
'title' => 'Blog Detail',
|
||||
'allowedAspectRatios' => [
|
||||
'1068:600' => [
|
||||
'title' => '1068:600',
|
||||
'value' => 1068 / 600,
|
||||
],
|
||||
],
|
||||
'selectedRatio' => '1068:600',
|
||||
'cropArea' => [
|
||||
'x' => 0.0,
|
||||
'y' => 0.0,
|
||||
'width' => 1.0,
|
||||
'height' => 1.0,
|
||||
],
|
||||
],
|
||||
'opengraph' => [
|
||||
'title' => 'Opengraph',
|
||||
'allowedAspectRatios' => [
|
||||
'1200:627' => [
|
||||
'title' => '1200:627',
|
||||
'value' => 1200 / 627,
|
||||
],
|
||||
],
|
||||
'selectedRatio' => '1200:627',
|
||||
'cropArea' => [
|
||||
'x' => 0.0,
|
||||
'y' => 0.0,
|
||||
'width' => 1.0,
|
||||
'height' => 1.0,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
]
|
||||
];
|
||||
103
Configuration/TCA/Overrides/sys_category.php
Normal file
@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
use Evoweb\EwBloggy\Constants;
|
||||
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||
|
||||
$languageFile = 'LLL:EXT:blog/Resources/Private/Language/locallang_db.xlf:';
|
||||
|
||||
// Add category types
|
||||
$GLOBALS['TCA']['sys_category']['ctrl']['type'] = 'record_type';
|
||||
$GLOBALS['TCA']['sys_category']['ctrl']['typeicon_column'] = 'record_type';
|
||||
$GLOBALS['TCA']['sys_category']['ctrl']['typeicon_classes'][(string) Constants::CATEGORY_TYPE_BLOG] = 'record-blog-category';
|
||||
|
||||
ExtensionManagementUtility::addToAllTCAtypes(
|
||||
'sys_category',
|
||||
'record_type',
|
||||
'',
|
||||
'before:title'
|
||||
);
|
||||
|
||||
$GLOBALS['TCA']['sys_category']['types'][(string) Constants::CATEGORY_TYPE_BLOG] =
|
||||
$GLOBALS['TCA']['sys_category']['types'][1];
|
||||
|
||||
// Limit parent categories to blog types
|
||||
$GLOBALS['TCA']['sys_category']['types'][Constants::CATEGORY_TYPE_BLOG]['columnsOverrides'] = [
|
||||
'parent' => [
|
||||
'config' => [
|
||||
'foreign_table_where' => ' AND sys_category.record_type = ' . Constants::CATEGORY_TYPE_BLOG . ' ' .
|
||||
' AND sys_category.pid = ###CURRENT_PID### ' .
|
||||
($GLOBALS['TCA']['pages']['columns']['categories']['config']['foreign_table_where'] ?? '')
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
// Register fields
|
||||
$GLOBALS['TCA']['sys_category']['columns'] = array_replace_recursive(
|
||||
$GLOBALS['TCA']['sys_category']['columns'],
|
||||
[
|
||||
'record_type' => [
|
||||
'label' => $languageFile . 'sys_category.record_type',
|
||||
'config' => [
|
||||
'type' => 'select',
|
||||
'renderType' => 'selectSingle',
|
||||
'items' => [
|
||||
[
|
||||
'label' => 'LLL:EXT:blog/Resources/Private/Language/locallang_tca.xlf:sys_category.record_type.blog',
|
||||
'value' => (string) Constants::CATEGORY_TYPE_BLOG,
|
||||
'icon' => 'record-blog-category'
|
||||
]
|
||||
],
|
||||
'default' => (string) Constants::CATEGORY_TYPE_BLOG,
|
||||
]
|
||||
],
|
||||
'slug' => [
|
||||
'label' => $languageFile . 'sys_category.slug',
|
||||
'config' => [
|
||||
'type' => 'slug',
|
||||
'generatorOptions' => [
|
||||
'fields' => ['title'],
|
||||
'replacements' => [
|
||||
'/' => ''
|
||||
],
|
||||
],
|
||||
'fallbackCharacter' => '-',
|
||||
'eval' => 'uniqueInSite',
|
||||
'default' => '',
|
||||
'max' => '2048',
|
||||
]
|
||||
],
|
||||
'posts' => [
|
||||
'label' => $languageFile . 'sys_category.posts',
|
||||
'config' => [
|
||||
'type' => 'group',
|
||||
'size' => 5,
|
||||
'allowed' => 'pages',
|
||||
'foreign_table' => 'pages',
|
||||
'MM' => 'sys_category_record_mm',
|
||||
'MM_match_fields' => [
|
||||
'fieldname' => 'categories',
|
||||
'tablenames' => 'pages',
|
||||
],
|
||||
'maxitems' => 1000
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
// Add slug field to all types
|
||||
ExtensionManagementUtility::addToAllTCAtypes(
|
||||
'sys_category',
|
||||
'slug',
|
||||
'',
|
||||
'after:title'
|
||||
);
|
||||
|
||||
// Add blog specific fields to blog categories
|
||||
ExtensionManagementUtility::addToAllTCAtypes(
|
||||
'sys_category',
|
||||
'
|
||||
--div--;' . $languageFile . 'sys_category.tabs.blog,
|
||||
posts
|
||||
',
|
||||
(string) Constants::CATEGORY_TYPE_BLOG
|
||||
);
|
||||
10
Configuration/TCA/Overrides/sys_template.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
// Add static templates
|
||||
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||
|
||||
ExtensionManagementUtility::addStaticFile(
|
||||
'ew_bloggy',
|
||||
'Configuration/TypoScript/',
|
||||
'Simple and small blog'
|
||||
);
|
||||
43
Configuration/TCA/Overrides/tt_content.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||
|
||||
$languageFile = 'LLL:EXT:ew_bloggy/Resources/Private/Language/locallang_db.xlf:';
|
||||
|
||||
ExtensionManagementUtility::addTcaSelectItemGroup(
|
||||
'tt_content',
|
||||
'CType',
|
||||
'ew_bloggy',
|
||||
$languageFile . 'CType.div.ew_bloggy'
|
||||
);
|
||||
|
||||
foreach (['posts', 'latestposts', 'archive'] as $type) {
|
||||
$contentTypeName = 'ewbloggy_' . $type;
|
||||
ExtensionManagementUtility::addRecordType(
|
||||
[
|
||||
'label' => $languageFile . 'plugin.' . $contentTypeName . '.title',
|
||||
'description' => $languageFile . 'plugin.' . $contentTypeName . '.description',
|
||||
'value' => $contentTypeName,
|
||||
'icon' => 'plugin-blog-' . $type,
|
||||
'group' => 'ew_bloggy'
|
||||
],
|
||||
'
|
||||
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general,
|
||||
--palette--;;general,
|
||||
--palette--;;headers,
|
||||
pi_flexform,
|
||||
pages;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:pages.ALT.list_formlabel,
|
||||
--div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.appearance,
|
||||
--palette--;;frames,
|
||||
--palette--;;appearanceLinks,
|
||||
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:categories,
|
||||
categories,
|
||||
',
|
||||
);
|
||||
|
||||
ExtensionManagementUtility::addPiFlexFormValue(
|
||||
'*',
|
||||
'FILE:EXT:ew_bloggy/Configuration/FlexForms/' . $type . '.xml',
|
||||
$contentTypeName
|
||||
);
|
||||
}
|
||||
177
Configuration/TCA/tx_ewbloggy_domain_model_author.php
Normal file
@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
$languageFile = 'LLL:EXT:ew_bloggy/Resources/Private/Language/locallang_db.xlf:';
|
||||
|
||||
return [
|
||||
'ctrl' => [
|
||||
'label' => 'name',
|
||||
'tstamp' => 'tstamp',
|
||||
'crdate' => 'crdate',
|
||||
'title' => $languageFile . 'tx_ewbloggy_domain_model_author',
|
||||
'default_sortby' => 'ORDER BY title',
|
||||
'delete' => 'deleted',
|
||||
'transOrigPointerField' => 'l18n_parent',
|
||||
'transOrigDiffSourceField' => 'l18n_diffsource',
|
||||
'languageField' => 'sys_language_uid',
|
||||
'enablecolumns' => [
|
||||
'disabled' => 'hidden',
|
||||
],
|
||||
'typeicon_classes' => [
|
||||
'default' => 'record-blog-author'
|
||||
],
|
||||
'searchFields' => 'uid,name,title',
|
||||
],
|
||||
'columns' => [
|
||||
'name' => [
|
||||
'label' => $languageFile . 'tx_ewbloggy_domain_model_author.name',
|
||||
'l10n_mode' => 'exclude',
|
||||
'l10n_display' => 'defaultAsReadonly',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 30,
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
'title' => [
|
||||
'label' => $languageFile . 'tx_ewbloggy_domain_model_author.title',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 30,
|
||||
],
|
||||
],
|
||||
'slug' => [
|
||||
'label' => $languageFile . 'tx_ewbloggy_domain_model_author.slug',
|
||||
'config' => [
|
||||
'type' => 'slug',
|
||||
'size' => 50,
|
||||
'generatorOptions' => [
|
||||
'fields' => ['name'],
|
||||
'replacements' => [
|
||||
'/' => ''
|
||||
],
|
||||
],
|
||||
'fallbackCharacter' => '-',
|
||||
'eval' => 'uniqueInSite',
|
||||
'default' => ''
|
||||
],
|
||||
],
|
||||
'image' => [
|
||||
'label' => $languageFile . 'tx_ewbloggy_domain_model_author.image',
|
||||
'l10n_mode' => 'exclude',
|
||||
'config' => [
|
||||
'type' => 'file',
|
||||
'allowed' => 'common-image-types',
|
||||
'appearance' => [
|
||||
'createNewRelationLinkTitle' =>
|
||||
'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:images.addFileReference'
|
||||
],
|
||||
'overrideChildTca' => [
|
||||
'types' => [
|
||||
\TYPO3\CMS\Core\Resource\FileType::IMAGE->value => [
|
||||
'showitem' => '
|
||||
--palette--;;imageoverlayPalette,
|
||||
--palette--;;filePalette',
|
||||
],
|
||||
],
|
||||
],
|
||||
'maxitems' => 1,
|
||||
],
|
||||
],
|
||||
'email' => [
|
||||
'label' => $languageFile . 'tx_ewbloggy_domain_model_author.email',
|
||||
'l10n_mode' => 'exclude',
|
||||
'config' => [
|
||||
'type' => 'email',
|
||||
'size' => 30,
|
||||
],
|
||||
],
|
||||
'website' => [
|
||||
'label' => $languageFile . 'tx_ewbloggy_domain_model_author.website',
|
||||
'l10n_mode' => 'exclude',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 30,
|
||||
'eval' => 'domainname',
|
||||
],
|
||||
],
|
||||
'profile' => [
|
||||
'label' => $languageFile . 'tx_ewbloggy_domain_model_author.profile',
|
||||
'l10n_mode' => 'exclude',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 30,
|
||||
'eval' => 'domainname',
|
||||
],
|
||||
],
|
||||
'bio' => [
|
||||
'label' => $languageFile . 'tx_ewbloggy_domain_model_author.bio',
|
||||
'config' => [
|
||||
'type' => 'text',
|
||||
'eval' => 'trim',
|
||||
],
|
||||
],
|
||||
'posts' => [
|
||||
'label' => $languageFile . 'tx_ewbloggy_domain_model_author.posts',
|
||||
'l10n_mode' => 'exclude',
|
||||
'config' => [
|
||||
'type' => 'select',
|
||||
'renderType' => 'selectMultipleSideBySide',
|
||||
'multiple' => 0,
|
||||
'foreign_table' => 'pages',
|
||||
'foreign_table_where' => 'AND {#pages}.{#doktype}=' . \Evoweb\EwBloggy\Constants::DOKTYPE_BLOG_POST
|
||||
. ' AND {#pages}.{#sys_language_uid} IN (-1,0)',
|
||||
'MM' => 'tx_ewbloggy_post_author_mm',
|
||||
'MM_opposite_field' => 'authors',
|
||||
'minitems' => 0,
|
||||
'maxitems' => 99999,
|
||||
],
|
||||
],
|
||||
'details_page' => [
|
||||
'label' => $languageFile . 'tx_ewbloggy_domain_model_author.details_page',
|
||||
'l10n_mode' => 'exclude',
|
||||
'config' => [
|
||||
'type' => 'group',
|
||||
'allowed' => 'pages',
|
||||
'size' => 1,
|
||||
'maxitems' => 1,
|
||||
'minitems' => 0,
|
||||
'default' => 0
|
||||
],
|
||||
]
|
||||
],
|
||||
'types' => [
|
||||
0 => [
|
||||
'showitem' => '
|
||||
--div--;' . $languageFile . 'tx_ewbloggy_domain_model_author.tab_profile,
|
||||
--palette--;' . $languageFile . 'tx_ewbloggy_domain_model_author.palette_personal;personal,
|
||||
slug,
|
||||
image,
|
||||
--palette--;' . $languageFile . 'tx_ewbloggy_domain_model_author.palette_contact;contact,
|
||||
profile,
|
||||
--div--;' . $languageFile . 'tx_ewbloggy_domain_model_author.tab_blog,
|
||||
posts,
|
||||
details_page,
|
||||
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access,
|
||||
--palette--;;hidden
|
||||
',
|
||||
],
|
||||
],
|
||||
'palettes' => [
|
||||
'hidden' => [
|
||||
'showitem' => '
|
||||
hidden;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:field.default.hidden
|
||||
',
|
||||
],
|
||||
'language' => [
|
||||
'showitem' => '
|
||||
sys_language_uid,l18n_parent
|
||||
',
|
||||
],
|
||||
'personal' => [
|
||||
'showitem' => 'name, title'
|
||||
],
|
||||
'contact' => [
|
||||
'showitem' => 'website, email'
|
||||
],
|
||||
],
|
||||
];
|
||||
5
Configuration/TypoScript/constants.typoscript
Normal file
@ -0,0 +1,5 @@
|
||||
plugin {
|
||||
tx_ewbloggy {
|
||||
limit = 3
|
||||
}
|
||||
}
|
||||
7
Configuration/TypoScript/setup.typoscript
Normal file
@ -0,0 +1,7 @@
|
||||
plugin {
|
||||
tx_ewbloggy {
|
||||
settings {
|
||||
limit = {$plugin.tx_ewbloggy.limit ?? $evoweb.tx-ewbloggy.limit}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
README.md
Normal file
@ -0,0 +1,8 @@
|
||||
# TYPO3 Extension ``ew_bloggy``
|
||||
|
||||
This extension is highly inspired by ``t3g/blog`` part of the code was copied
|
||||
from it and composed to a blog that is limited to latest post, post list and
|
||||
archive.
|
||||
|
||||
The extension tries to integrate every new feature of TYPO3 13 to make the
|
||||
integration easier to me.
|
||||
29
Resources/Private/Language/locallang.xlf
Normal file
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<file source-language="en" datatype="plaintext" original="messages" date="2025-01-17T21:47:12Z" product-name="ew_bloggy">
|
||||
<header/>
|
||||
<body>
|
||||
<trans-unit id="pagelayout.section.published" xml:space="preserve">
|
||||
<source>Published</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pagelayout.section.authors" xml:space="preserve">
|
||||
<source>Authors</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pagelayout.message.noauthor" xml:space="preserve">
|
||||
<source>No author assigned.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pagelayout.section.featuredimage" xml:space="preserve">
|
||||
<source>Featured Image</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pagelayout.message.nofeaturedimage" xml:space="preserve">
|
||||
<source>No featured image assigned.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pagelayout.section.categories" xml:space="preserve">
|
||||
<source>Categories</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pagelayout.button.post.edit" xml:space="preserve">
|
||||
<source>Edit Post Meta-Data</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
136
Resources/Private/Language/locallang_db.xlf
Normal file
@ -0,0 +1,136 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
|
||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<file source-language="en" datatype="plaintext" original="messages" date="2025-01-17T14:18:34Z" product-name="ew_bloggy">
|
||||
<header/>
|
||||
<body>
|
||||
|
||||
<trans-unit id="CType.div.ew_bloggy" xml:space="preserve">
|
||||
<source>Bloggy</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="plugin.ewbloggy_posts.title" xml:space="preserve">
|
||||
<source>Bloggy: List of posts</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="plugin.ewbloggy_posts.description" xml:space="preserve">
|
||||
<source>Displays a list of blog posts ordered by date. All non-hidden, non-deleted and non-archived posts are shown in the list.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="plugin.ewbloggy_latestposts.title" xml:space="preserve">
|
||||
<source>Bloggy: Latest posts</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="plugin.ewbloggy_latestposts.description" xml:space="preserve">
|
||||
<source>Displays a number of latest posts. You can specify the amount of items yourself.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="latestpost">
|
||||
<source>Latest posts settings</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="plugin.ewbloggy_archive.title" xml:space="preserve">
|
||||
<source>Bloggy: Archive</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="plugin.ewbloggy_archive.description" xml:space="preserve">
|
||||
<source>The archive plugin displays all posts categorized by year and month.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="plugin.ewbloggy_header.title" xml:space="preserve">
|
||||
<source>Bloggy: Header</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="plugin.ewbloggy_header.description" xml:space="preserve">
|
||||
<source>Displays post header</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="plugin.ewbloggy_footer.title" xml:space="preserve">
|
||||
<source>Bloggy: Footer</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="plugin.ewbloggy_footer.description" xml:space="preserve">
|
||||
<source>Displays post footer</source>
|
||||
</trans-unit>
|
||||
|
||||
<trans-unit id="pages.tabs.blog" xml:space="preserve">
|
||||
<source>Blog</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.palettes.publish_date" xml:space="preserve">
|
||||
<source>Publish date</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.publish_date" xml:space="preserve">
|
||||
<source>Publish date</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.crdate_month" xml:space="preserve">
|
||||
<source>Publish date month</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.crdate_year" xml:space="preserve">
|
||||
<source>Publish date year</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.archive_date" xml:space="preserve">
|
||||
<source>Archive date</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.featured_image" xml:space="preserve">
|
||||
<source>Featured Image</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="pages.authors" xml:space="preserve">
|
||||
<source>Authors</source>
|
||||
</trans-unit>
|
||||
|
||||
<trans-unit id="tx_ewbloggy_domain_model_author" xml:space="preserve">
|
||||
<source>Blog: Author</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_ewbloggy_domain_model_author.name" xml:space="preserve">
|
||||
<source>Name</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_ewbloggy_domain_model_author.slug" xml:space="preserve">
|
||||
<source>Path Segment</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_ewbloggy_domain_model_author.image" xml:space="preserve">
|
||||
<source>Image</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_ewbloggy_domain_model_author.title" xml:space="preserve">
|
||||
<source>Title</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_ewbloggy_domain_model_author.website" xml:space="preserve">
|
||||
<source>Website</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_ewbloggy_domain_model_author.email" xml:space="preserve">
|
||||
<source>Email</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_ewbloggy_domain_model_author.profile" xml:space="preserve">
|
||||
<source>Author profile URL (used for schema.org markup)</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_ewbloggy_domain_model_author.bio" xml:space="preserve">
|
||||
<source>Bio</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_ewbloggy_domain_model_author.posts" xml:space="preserve">
|
||||
<source>Blog posts</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_ewbloggy_domain_model_author.details_page" xml:space="preserve">
|
||||
<source>Details page</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_ewbloggy_domain_model_author.palette_personal" xml:space="preserve">
|
||||
<source>Personal data</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_ewbloggy_domain_model_author.palette_contact" xml:space="preserve">
|
||||
<source>Contact data</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_ewbloggy_domain_model_author.tab_profile" xml:space="preserve">
|
||||
<source>Profile</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="tx_ewbloggy_domain_model_author.tab_blog" xml:space="preserve">
|
||||
<source>Blog data</source>
|
||||
</trans-unit>
|
||||
|
||||
<trans-unit id="sys_category.record_type" xml:space="preserve">
|
||||
<source>Record Type</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sys_category.slug" xml:space="preserve">
|
||||
<source>Path Segment</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sys_category.posts" xml:space="preserve">
|
||||
<source>Posts</source>
|
||||
</trans-unit>
|
||||
|
||||
<trans-unit id="settings.flexform.limit">
|
||||
<source>Limit</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="settings.flexform.postsUid">
|
||||
<source>Posts page uid</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="settings.flexform.archiveUid">
|
||||
<source>Archive page uid</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
10
Resources/Private/Layouts/Default.html
Normal file
@ -0,0 +1,10 @@
|
||||
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||
|
||||
<div class="
|
||||
relative
|
||||
min-h-screen
|
||||
">
|
||||
<f:render section="Content" />
|
||||
</div>
|
||||
|
||||
</html>
|
||||
104
Resources/Private/Templates/PageLayout/Header.html
Normal file
@ -0,0 +1,104 @@
|
||||
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
|
||||
xmlns:ew="http://typo3.org/ns/Evoweb/EwBloggy/ViewHelpers"
|
||||
data-namespace-typo3-fluid="true">
|
||||
<f:if condition="{post}">
|
||||
<f:asset.css identifier="ew-bloggy-pagelayout" href="EXT:ew_bloggy/Resources/Public/Css/pagelayout.min.css"/>
|
||||
<div class="
|
||||
tw-relative
|
||||
tw-bg-white dark:tw-bg-component-bg
|
||||
tw-rounded-component
|
||||
shadow-component
|
||||
tw-m-component
|
||||
tw-border-solid
|
||||
tw-border-l-12 tw-border-r-0 tw-border-b-0 tw-border-t-0
|
||||
tw-border-green-600
|
||||
">
|
||||
<div class="tw-absolute tw-top-5 tw-left-4">
|
||||
<core:iconForRecord table="pages" row="{post.asArray}" size="large" />
|
||||
</div>
|
||||
|
||||
<div class="tw-relative tw-p-5 tw-pl-19 tw-min-h-19">
|
||||
<div class="[&_>_*:first-child]:tw-mt-0 [&_>_*:last-child]:tw-mb-0">
|
||||
<p><f:if condition="{post.abstract} || {post.description}">
|
||||
<f:then>{f:if(condition: post.abstract, then: post.abstract, else: post.description)}</f:then>
|
||||
<f:else><f:translate key="pagelayout.message.notabstractordescription" extensionName="ew_bloggy" /></f:else>
|
||||
</f:if></p>
|
||||
</div>
|
||||
|
||||
<div class="tw-grid tw-gap-2.5 tw-grid-cols-component">
|
||||
<div class="tw-mt-5">
|
||||
<h3 class="tw-text-xs tw-font-bold tw-mt-0 tw-mb-1.5"><f:translate key="pagelayout.section.published" extensionName="ew_bloggy" /></h3>
|
||||
<div class="[&_>_*:first-child]:tw-mt-0 [&_>_*:last-child]:tw-mb-0">
|
||||
<time datetime="{f:format.date(format: '%Y-%m-%dT%H:%M:%S-%z', date: post.publishDate)}" itemprop="datepublished">{f:format.date(format: '%d.%m.%Y - %H:%M', date: post.publishDate)}</time>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tw-mt-5">
|
||||
<h3 class="tw-text-xs tw-font-bold tw-mt-0 tw-mb-1.5"><f:translate key="pagelayout.section.authors" extensionName="ew_bloggy" /></h3>
|
||||
<div class="[&_>_*:first-child]:tw-mt-0 [&_>_*:last-child]:tw-mb-0">
|
||||
<f:if condition="{post.authors}">
|
||||
<f:then>
|
||||
<ul class="tw-list-none tw-p-0">
|
||||
<f:for each="{post.authors}" as="author">
|
||||
<li>{author.name}</li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<p><f:translate key="pagelayout.message.noauthor" extensionName="ew_bloggy" /></p>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tw-mt-5">
|
||||
<h3 class="tw-text-xs tw-font-bold tw-mt-0 tw-mb-1.5"><f:translate key="pagelayout.section.featuredimage" extensionName="ew_bloggy" /></h3>
|
||||
<div class="[&_>_*:first-child]:tw-mt-0 [&_>_*:last-child]:tw-mb-0">
|
||||
<f:if condition="{post.featuredImage}">
|
||||
<f:then>
|
||||
<f:image loading="lazy" class="tw-h-auto tw-max-w-full" image="{post.featuredImage}" alt="{post.featuredImage.alternative}" title="{post.featuredImage.title}" height="100c" width="200" />
|
||||
</f:then>
|
||||
<f:else>
|
||||
<p><f:translate key="pagelayout.message.nofeaturedimage" extensionName="ew_bloggy" /></p>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<f:if condition="{post.tags}">
|
||||
<div class="tw-mt-5">
|
||||
<h3 class="tw-text-xs tw-font-bold tw-mt-0 tw-mb-1.5"><f:translate key="pagelayout.section.tags" extensionName="ew_bloggy" /></h3>
|
||||
<div class="[&_>_*:first-child]:tw-mt-0 [&_>_*:last-child]:tw-mb-0">
|
||||
<ul class="tw-list-none tw-p-0">
|
||||
<f:for each="{post.tags}" as="tag">
|
||||
<li class="tw-inline"><span class="badge badge-info">{tag.title}</span></li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</f:if>
|
||||
|
||||
<f:if condition="{post.categories}">
|
||||
<div class="tw-mt-5">
|
||||
<h3 class="tw-text-xs tw-font-bold tw-mt-0 tw-mb-1.5"><f:translate key="pagelayout.section.categories" extensionName="ew_bloggy" /></h3>
|
||||
<div class="[&_>_*:first-child]:tw-mt-0 [&_>_*:last-child]:tw-mb-0">
|
||||
<ul class="tw-list-none tw-p-0">
|
||||
<f:for each="{post.categories}" as="category">
|
||||
<li class="tw-inline"><span class="badge badge-info">{category.title}</span></li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</f:if>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tw-p-5 tw-pt-0">
|
||||
<ew:link.be.post post="{post}" action="edit" class="btn btn-default">
|
||||
<core:icon identifier="record-blog-post" />
|
||||
<f:translate key="pagelayout.button.post.edit" extensionName="ew_bloggy" />
|
||||
</ew:link.be.post>
|
||||
</div>
|
||||
</div>
|
||||
</f:if>
|
||||
</html>
|
||||
133
Resources/Private/Templates/Post/ListLatestPosts.html
Normal file
@ -0,0 +1,133 @@
|
||||
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||
|
||||
<f:layout name="Default" />
|
||||
|
||||
<f:section name="Content">
|
||||
<f:if condition="{posts.0} && {posts.0.featuredImage}">
|
||||
<f:asset.css identifier="blog_latest_featured_image">
|
||||
@media (max-width:1023px){ .bg-featured-image { --featured-image-url: url(<f:uri.image image="{posts.0.featuredImage}" cropVariant="latest" maxWidth="908" maxHeight="c320"/>); } }
|
||||
@media (min-width:1024px){ .bg-featured-image { --featured-image-url: url(<f:uri.image image="{posts.0.featuredImage}" cropVariant="latest" maxWidth="910" maxHeight="1080"/>); } }
|
||||
</f:asset.css>
|
||||
</f:if>
|
||||
|
||||
<div class="grid min-h-screen lg:grid-cols-2 dlg:min-h-screen">
|
||||
<div class="
|
||||
lg:order-last
|
||||
w-full
|
||||
h-full
|
||||
min-h-80
|
||||
bg-cover bg-center bg-featured-image">
|
||||
</div>
|
||||
|
||||
<div class="
|
||||
group
|
||||
max-w-117
|
||||
mx-auto pt-12 pb-24 lg:py-24">
|
||||
|
||||
<f:for each="{posts}" as="post" iteration="iterator">
|
||||
<f:if condition="{iterator.isFirst}">
|
||||
<f:then><f:render section="firstPost" arguments="{_all}"/></f:then>
|
||||
<f:else><f:render section="otherPosts" arguments="{_all}"/></f:else>
|
||||
</f:if>
|
||||
</f:for>
|
||||
|
||||
<f:if condition="{posts -> f:count()} >= {settings.limit}">
|
||||
<div class="pt-24">
|
||||
<a href="{f:uri.page(pageUid: settings.postsUid)}"
|
||||
class="
|
||||
font-montserrat text-green-dark font-bold
|
||||
text-lg leading-9 uppercase
|
||||
block
|
||||
">Weitere Beiträge</a>
|
||||
</div>
|
||||
</f:if>
|
||||
</div>
|
||||
</div>
|
||||
</f:section>
|
||||
|
||||
<f:section name="firstPost">
|
||||
<article id="post-{post.uid}" class="
|
||||
transition duration-200 ease-in-out
|
||||
group-hover:blur-[2px] group-hover:hover:blur-none
|
||||
pb-4 lg:pb-28
|
||||
">
|
||||
<div class="flex font-montserrat text-sm leading-9 font-bold uppercase">
|
||||
<f:if condition="{post.categories.0}">
|
||||
<ul>
|
||||
<li class="
|
||||
inline-block mr-1.5
|
||||
after:content-[','] last:after:content-['']
|
||||
">
|
||||
<span class="text-green-dark">{post.categories.0.title}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</f:if>
|
||||
<div class="
|
||||
flex-auto
|
||||
published
|
||||
text-gray-800
|
||||
before:content-['\2022']
|
||||
before:inline-block
|
||||
before:mr-1.5
|
||||
">
|
||||
<f:format.date format="%e. %B %Y">{post.publishDate}</f:format.date>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 class="font-joschmi font-bold text-5xl leading-13 tracking-wider pt-0 pb-10 lg:pt-10">
|
||||
<a href="{f:uri.page(pageUid: post.uid)}"
|
||||
class="text-gray-1300 hover:text-green-dark">{post.title}</a>
|
||||
</h1>
|
||||
<div class="excerpt font-libre text-base leading-7 text-gray-800">
|
||||
<f:format.crop maxCharacters="300" respectWordBoundaries="1"><f:if condition="{post.abstract}"><f:then>{post.abstract}</f:then><f:else>{post.description}</f:else></f:if></f:format.crop>
|
||||
<a href="{f:uri.page(pageUid: post.uid)}"
|
||||
title="{post.title}"
|
||||
class="
|
||||
inline-block
|
||||
w-8 h-8
|
||||
ml-4
|
||||
align-text-top
|
||||
text-green
|
||||
">
|
||||
<i class="fal fa-arrow-right fa-2x"></i>
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
</f:section>
|
||||
|
||||
<f:section name="otherPosts">
|
||||
<article id="post-{post.uid}" class="
|
||||
transition duration-200 ease-in-out
|
||||
group-hover:blur-[2px] group-hover:hover:blur-none
|
||||
">
|
||||
<h2 class="font-montserrat font-bold text-3xl pt-8 pb-0">
|
||||
<a href="{f:uri.page(pageUid: post.uid)}"
|
||||
class="text-gray-1300 hover:text-green-dark">{post.title}</a>
|
||||
</h2>
|
||||
|
||||
<div class="flex font-montserrat text-sm leading-9 font-bold uppercase">
|
||||
<f:if condition="{post.categories.0}">
|
||||
<ul class="cat-links flex-none">
|
||||
<li class="
|
||||
inline-block mr-1.5
|
||||
after:content-[','] last:after:content-['']
|
||||
">
|
||||
<span class="text-green-dark">{post.categories.0.title}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</f:if>
|
||||
<div class="
|
||||
flex-auto
|
||||
published
|
||||
text-gray-800
|
||||
before:content-['\2022']
|
||||
before:inline-block
|
||||
before:mr-1.5
|
||||
">
|
||||
<f:format.date format="%e. %B %Y">{post.publishDate}</f:format.date>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</f:section>
|
||||
|
||||
</html>
|
||||
54
Resources/Private/Templates/Post/ListPostsByDate.html
Normal file
@ -0,0 +1,54 @@
|
||||
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
|
||||
xmlns:ew="http://typo3.org/ns/Evoweb/EwBloggy/ViewHelpers"
|
||||
data-namespace-typo3-fluid="true">
|
||||
|
||||
<f:layout name="Default" />
|
||||
|
||||
<f:section name="Content">
|
||||
<f:if condition="!{archiveData}">
|
||||
<f:then>
|
||||
|
||||
<f:comment><!--If a year/+month is selected show posts matching.--></f:comment>
|
||||
<header class="blogarchiveheader blogarchiveheader--archive">
|
||||
<h1 class="blogarchiveheader__title">
|
||||
<span class="blogarchiveheader__titletext">
|
||||
<f:if condition="{month}">{f:format.date(format: '{settings.widgets.archive.monthDateFormat}', date: timestamp)}</f:if> {year}
|
||||
</span>
|
||||
<ew:link.archive class="blogarchiveheader__titlelink" rss="true" year="{year}" month="{month}">
|
||||
<span class="blogicon"><f:render partial="General/SocialIcons" section="Rss" optional="true" /></span>
|
||||
</ew:link.archive>
|
||||
</h1>
|
||||
</header>
|
||||
<f:render partial="List" arguments="{_all}" />
|
||||
<f:if condition="{settings.authorUid}">
|
||||
<footer class="blogarchivefooter blogarchivefooter--archive">
|
||||
<div class="blogarchivefooter__backlink">
|
||||
<f:link.page pageUid="{settings.archiveUid}">
|
||||
<f:translate key="list.backlink.archive" />
|
||||
</f:link.page>
|
||||
</div>
|
||||
</footer>
|
||||
</f:if>
|
||||
|
||||
</f:then>
|
||||
<f:else>
|
||||
|
||||
<f:comment><!--If year is missing show an overview of the archive.--></f:comment>
|
||||
<header class="blogarchiveheader blogarchiveheader--archive">
|
||||
<h1 class="blogarchiveheader__title">
|
||||
<span class="blogarchiveheader__titletext">
|
||||
<f:translate key="headline.archive"/>
|
||||
</span>
|
||||
</h1>
|
||||
</header>
|
||||
<div class="bloglist bloglist--archive">
|
||||
<f:for each="{archiveData}" as="months" key="year">
|
||||
<f:render partial="List/Archive" arguments="{_all}" />
|
||||
</f:for>
|
||||
</div>
|
||||
|
||||
</f:else>
|
||||
</f:if>
|
||||
</f:section>
|
||||
|
||||
</html>
|
||||
53
Resources/Private/Templates/Post/ListRecentPosts.html
Normal file
@ -0,0 +1,53 @@
|
||||
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
|
||||
|
||||
<f:layout name="Default" />
|
||||
|
||||
<f:section name="Content">
|
||||
<ol class="list-articles py-4">
|
||||
<f:for each="{posts}" as="post">
|
||||
<f:render section="post" arguments="{_all}"/>
|
||||
</f:for>
|
||||
</ol>
|
||||
</f:section>
|
||||
|
||||
<f:section name="post">
|
||||
<li class="
|
||||
grid
|
||||
grid-cols-[9rem_auto] grid-rows-[repeat(2,auto)]
|
||||
gap-x-5
|
||||
border-gray-1200 border-solid
|
||||
border-b last:border-none
|
||||
py-4
|
||||
">
|
||||
<f:if condition="{post.featuredImage}">
|
||||
<f:then>
|
||||
<f:link.page pageUid="{post.uid}" title="{post.title}" class="row-span-full">
|
||||
<img src="{f:uri.image(image: post.featuredImage, width: 140, height: 140, cropVariant: 'list')}"
|
||||
alt="{post.featuredImage.alternative}"
|
||||
class="w-36 h-36"/>
|
||||
</f:link.page>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<div class="block w-36"></div>
|
||||
</f:else>
|
||||
</f:if>
|
||||
<header class="row-span-auto">
|
||||
<f:link.page pageUid="{post.uid}">
|
||||
<f:if condition="{post.categories.0}">
|
||||
<h3 class="font-montserrat text-lg">{post.categories.0.title}</h3>
|
||||
</f:if>
|
||||
<h2 class="font-montserrat font-bold text-xl">{post.title}</h2>
|
||||
</f:link.page>
|
||||
</header>
|
||||
<f:if condition="{post.abstract} || {post.description}">
|
||||
<div class="row-span-auto">
|
||||
<f:format.html><f:format.crop maxCharacters="300" respectWordBoundaries="1"><f:if condition="{post.abstract}">
|
||||
<f:then>{post.abstract}</f:then>
|
||||
<f:else>{post.description}</f:else>
|
||||
</f:if></f:format.crop></f:format.html>
|
||||
</div>
|
||||
</f:if>
|
||||
</li>
|
||||
</f:section>
|
||||
|
||||
</html>
|
||||
1
Resources/Public/Css/pagelayout.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.tw-absolute{position:absolute}.tw-relative{position:relative}.tw-left-4{left:1rem}.tw-top-5{top:1.25rem}.tw-m-component{margin:var(--typo3-spacing,1em) auto}.tw-mb-1{margin-bottom:.25rem}.tw-mb-1\.5{margin-bottom:.375rem}.tw-mt-0{margin-top:0}.tw-mt-5{margin-top:1.25rem}.tw-inline{display:inline}.tw-grid{display:grid}.tw-h-auto{height:auto}.tw-min-h-19{min-height:4.75rem}.tw-max-w-full{max-width:100%}.tw-list-none{list-style-type:none}.tw-grid-cols-component{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}.tw-gap-2\.5{gap:.625rem}.tw-rounded-component{border-radius:var(--typo3-component-border-radius)}.tw-border-b-0{border-bottom-width:0}.tw-border-l-12{border-left-width:12px}.tw-border-r-0{border-right-width:0}.tw-border-t-0{border-top-width:0}.tw-border-solid{border-style:solid}.tw-border-green-600{--tw-border-opacity:1;border-color:rgb(22 163 74/var(--tw-border-opacity,1))}.tw-bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.tw-p-0{padding:0}.tw-p-5{padding:1.25rem}.tw-pl-19{padding-left:4.75rem}.tw-pt-0{padding-top:0}.tw-text-xs{font-size:.75rem;line-height:1rem}.tw-font-bold{font-weight:700}.shadow-component{box-shadow:var(--typo3-component-box-shadow)}@media (prefers-color-scheme:dark){.dark\:tw-bg-component-bg{background-color:var(--typo3-component-bg)}}.\[\&_\>_\*\:first-child\]\:tw-mt-0>:first-child{margin-top:0}.\[\&_\>_\*\:last-child\]\:tw-mb-0>:last-child{margin-bottom:0}
|
||||
5
Resources/Public/Icons/Extension.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg id="svg2" xmlns="http://www.w3.org/2000/svg" height="9in" width="9in" version="1.1" viewBox="0 0 640 640">
|
||||
<path id="Auswahl1" d="m4.73 377l-4.73-45c-1.17-100.04 31.51-196.72 110-262.56 42.12-35.33 96.7-58.01 151-65.57l36-3.87h49s38 4.58 38 4.58c35.73 5.54 72.58 17.87 104 35.85 53.09 30.37 91.35 72.89 118.24 127.57 23.12 47.01 33.76 98.83 33.76 151h-555l1 18c0.07 13.74 5.29 37.39 8.88 51l3.12 11-81 29c-5.4-12.32-10.25-37.27-12.27-51zm489.43-221s-8.16-8.91-8.16-8.91c-34.3-32.33-78.39-52.53-125-58.81-9.87-1.33-20.05-2.26-30-2.28l-11-0.66s-17 0.66-17 0.66l-19 1.84c-35.79 3.82-69.99 15.91-100 35.83-30.13 20-55.03 47.97-71.25 80.33-4.56 9.1-12.24 25.24-13.75 35h443c-10.63-31.88-25.41-57.89-47.84-83z" fill="#87b200"/>
|
||||
<path id="Auswahl2" d="m143.72 481c12.28 14.12 25.74 25.64 41.28 36 3.08 2.05 12.54 8.89 16 8.28 2.62-0.46 6.12-4.4 8-6.28l15.09-16s56.91-57 56.91-57l29-29c2.36-2.36 7.37-8.57 11-7.66 2.27 0.56 6.26 4.92 8 6.66l18 18 64 64 21 21c2.17 2.17 5.77 7.05 9.17 6.3 1.5-0.33 7.18-3.9 8.83-4.9 8.04-4.85 15.75-10.51 23-16.48 30.57-25.21 51.43-56.38 66-92.92 4.35-10.9 8.34-24.51 10.79-36 0.59-2.78 1.84-12.51 3.8-13.96 2.4-1.78 13.18 0.83 16.41 1.37 0 0 47 8.18 47 8.18 3.47 0.58 16.03 1.63 17.36 4.72 1.15 2.68-2.21 17.15-3.01 20.69-4.17 18.44-9.52 36.44-16.55 54-27.05 67.63-74.22 122.45-139.8 155.25-10.16 5.07-28.35 13.19-39 16.4-7.8 2.36-9.68-1.34-15-6.65l-15-15s-63-64-63-64l-16-16c-1.63-1.6-4.58-4.96-7-4.96-2.73 0-7.1 5.06-9 6.96l-20 20s-16.01 17-16.01 17-37.99 38-37.99 38l-25 24.36c-4.38 2.57-10.64-0.84-15-2.36-10.8-3.75-27.07-10.98-37-16.58-39.58-22.32-67.36-44.68-94.87-81.42-6.36-8.48-21.15-29.57-24.13-39l49-28.42 25-13.58c8.24 15.58 17.18 27.74 28.72 41z" fill="#4f660b"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
1
Resources/Public/Icons/blog-author.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 32 32"><path fill="#87b200" d="M16 4a5 5 0 1 1-5 5a5 5 0 0 1 5-5m0-2a7 7 0 1 0 7 7a7 7 0 0 0-7-7m10 28h-2v-5a5 5 0 0 0-5-5h-6a5 5 0 0 0-5 5v5H6v-5a7 7 0 0 1 7-7h6a7 7 0 0 1 7 7z"/></svg>
|
||||
|
After Width: | Height: | Size: 265 B |
1
Resources/Public/Icons/blog-category.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 32 32"><path fill="#87b200" d="M27 22.141V18a2 2 0 0 0-2-2h-8v-4h2a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2h-6a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h2v4H7a2 2 0 0 0-2 2v4.142a4 4 0 1 0 2 0V18h8v4.142a4 4 0 1 0 2 0V18h8v4.141a4 4 0 1 0 2 0M13 4h6l.001 6H13ZM8 26a2 2 0 1 1-2-2a2 2 0 0 1 2 2m10 0a2 2 0 1 1-2-2a2.003 2.003 0 0 1 2 2m8 2a2 2 0 1 1 2-2a2 2 0 0 1-2 2"/></svg>
|
||||
|
After Width: | Height: | Size: 431 B |
1
Resources/Public/Icons/blog-post.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><path fill="#87b200" d="M172.2 226.8c-14.6-2.9-28.2 8.9-28.2 23.8V301c0 10.2 7.1 18.4 16.7 22c18.2 6.8 31.3 24.4 31.3 45c0 26.5-21.5 48-48 48s-48-21.5-48-48V120c0-13.3-10.7-24-24-24H24c-13.3 0-24 10.7-24 24v248c0 89.5 82.1 160.2 175 140.7c54.4-11.4 98.3-55.4 109.7-109.7c17.4-82.9-37-157.2-112.5-172.2M209 0c-9.2-.5-17 6.8-17 16v31.6c0 8.5 6.6 15.5 15 15.9c129.4 7 233.4 112 240.9 241.5c.5 8.4 7.5 15 15.9 15h32.1c9.2 0 16.5-7.8 16-17C503.4 139.8 372.2 8.6 209 0m.3 96c-9.3-.7-17.3 6.7-17.3 16.1v32.1c0 8.4 6.5 15.3 14.8 15.9c76.8 6.3 138 68.2 144.9 145.2c.8 8.3 7.6 14.7 15.9 14.7h32.2c9.3 0 16.8-8 16.1-17.3c-8.4-110.1-96.5-198.2-206.6-206.7"/></svg>
|
||||
|
After Width: | Height: | Size: 740 B |
1
Resources/Public/Icons/plugin-blog-archive.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 32 32"><circle cx="7" cy="9" r="3" fill="currentColor"/><circle cx="7" cy="23" r="3" fill="currentColor"/><path fill="currentColor" d="M16 22h14v2H16zm0-14h14v2H16z"/></svg>
|
||||
|
After Width: | Height: | Size: 252 B |
1
Resources/Public/Icons/plugin-blog-latestposts.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 32 32"><path fill="currentColor" d="M16 22h14v2H16zm0-14h14v2H16zm-8 4V4H6v1H4v2h2v5H4v2h6v-2zm2 16H4v-4a2 2 0 0 1 2-2h2v-2H4v-2h4a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H6v2h4z"/></svg>
|
||||
|
After Width: | Height: | Size: 255 B |
1
Resources/Public/Icons/plugin-blog-posts.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 32 32"><path fill="currentColor" d="M16 22h14v2H16zm-2-2.6L12.6 18L6 24.6L3.4 22L2 23.4l4 4zM16 8h14v2H16zm-2-2.6L12.6 4L6 10.6L3.4 8L2 9.4l4 4z"/></svg>
|
||||
|
After Width: | Height: | Size: 232 B |
19
composer.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "evoweb/ew-bloggy",
|
||||
"type": "typo3-cms-extension",
|
||||
"description": "Simple and small blog",
|
||||
"homepage": "https://www.evoweb.de/",
|
||||
"version": "0.0.1",
|
||||
"require": {
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Evoweb\\EwBloggy\\": "Classes/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"typo3/cms": {
|
||||
"extension-key": "ew_bloggy"
|
||||
}
|
||||
}
|
||||
}
|
||||
28
ext_localconf.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Evoweb\EwBloggy\Controller\PostController;
|
||||
use TYPO3\CMS\Extbase\Utility\ExtensionUtility;
|
||||
|
||||
ExtensionUtility::configurePlugin(
|
||||
'EwBloggy',
|
||||
'Posts',
|
||||
[ PostController::class => 'listRecentPosts' ],
|
||||
[],
|
||||
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
|
||||
);
|
||||
|
||||
ExtensionUtility::configurePlugin(
|
||||
'EwBloggy',
|
||||
'LatestPosts',
|
||||
[ PostController::class => 'listLatestPosts' ],
|
||||
[],
|
||||
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
|
||||
);
|
||||
|
||||
ExtensionUtility::configurePlugin(
|
||||
'EwBloggy',
|
||||
'Archive',
|
||||
[ PostController::class => 'listPostsByDate' ],
|
||||
[],
|
||||
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
|
||||
);
|
||||
14
ext_tables.sql
Normal file
@ -0,0 +1,14 @@
|
||||
#
|
||||
# Table structure for table 'pages'
|
||||
#
|
||||
CREATE TABLE pages (
|
||||
KEY post_crdate (crdate_year, crdate_month)
|
||||
);
|
||||
|
||||
|
||||
#
|
||||
# Table structure for table 'sys_category'
|
||||
#
|
||||
CREATE TABLE sys_category (
|
||||
record_type int(11) unsigned DEFAULT '1' NOT NULL
|
||||
);
|
||||