Skip to content

Commit

Permalink
v3.0.4-beta.31
Browse files Browse the repository at this point in the history
  • Loading branch information
OFFLINE committed May 28, 2023
1 parent 8b55bb6 commit 4004551
Show file tree
Hide file tree
Showing 107 changed files with 7,714 additions and 0 deletions.
16 changes: 16 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Copyright (C) OFFLINE GmbH - All Rights Reserved.

THE CONTENTS OF THIS PROJECT ARE PROPRIETARY.

You may use this plugin in October CMS based projects only. You are allowed to distribute this plugin to your clients
as part of your work. You are not allowed to resell this plugin or any part of it on its own.

The pro version of this plugin requires a valid October CMS plugin license from the October CMS marketplace for every project.

The software is provided "AS IS", without warranty of any kind, express or implied, including but not limited to
the warranties of merchantability, fitness for a particular purpose and non infringement.
In no event shall the authors or copyright holders be liable for any claim, damages or other liability,
whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software
or the use or other dealings in the software.

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
349 changes: 349 additions & 0 deletions Plugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,349 @@
<?php

namespace OFFLINE\Boxes;

use Backend;
use Cms\Classes\Controller as CmsController;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Lang;
use October\Rain\Database\Scopes\MultisiteScope;
use October\Rain\Support\Facades\Block;
use October\Rain\Support\Facades\Config;
use October\Rain\Support\Facades\Event;
use October\Rain\Support\Facades\Site;
use OFFLINE\Boxes\Classes\CMS\CmsPageParams;
use OFFLINE\Boxes\Classes\CMS\Controller;
use OFFLINE\Boxes\Classes\Features;
use OFFLINE\Boxes\Classes\Partial\ExternalPartial;
use OFFLINE\Boxes\Classes\Partial\Partial;
use OFFLINE\Boxes\Classes\PatchedTreeCollection;
use OFFLINE\Boxes\Classes\Search\SiteSearch;
use OFFLINE\Boxes\Components\BoxesPage;
use OFFLINE\Boxes\Components\BoxesPageEditor;
use OFFLINE\Boxes\FormWidgets\BoxesDataForm;
use OFFLINE\Boxes\FormWidgets\BoxesEditor;
use OFFLINE\Boxes\FormWidgets\BoxFinder;
use OFFLINE\Boxes\FormWidgets\HiddenInput;
use OFFLINE\Boxes\Models\Box;
use OFFLINE\Boxes\Models\BoxesSetting;
use OFFLINE\Boxes\Models\Content;
use OFFLINE\Boxes\Models\Page;
use System\Classes\PluginBase;
use Url;

/**
* Plugin Information File
*/
class Plugin extends PluginBase
{
public function __construct($app)
{
parent::__construct($app);

// RainLab.Translate is an optional dependency.
// But if it is enabled, we need to require it to make sure
// that all migrations are run in the right order.
if (class_exists(\RainLab\Translate\Plugin::class)) {
$this->require[] = 'RainLab.Translate';
}
}

/**
* Returns information about this plugin.
*
* @return array
*/
public function pluginDetails()
{
return [
'name' => 'Boxes Free',
'description' => 'Visual Page Builder for October CMS',
'author' => 'OFFLINE',
'icon' => 'icon-cube',
];
}

/**
* Register method, called when the plugin is first registered.
*
* @return void
*/
public function register()
{

}

/**
* Boot method, called right before the request route.
*
* @return void
*/
public function boot()
{
$basePath = App::basePath();

Event::listen('cms.page.beforeRenderPartial', function ($controller, $partial) use ($basePath) {
// Allow external partials to be loaded by the CMS. This allows
// third-party plugins to provide custom partials for the plugin.
if (starts_with($partial, Partial::EXTERNAL_PREFIX)) {
$filename = str_replace(Partial::EXTERNAL_PREFIX, '', $partial);

if (file_exists($filename) && starts_with($filename, $basePath)) {
return ExternalPartial::load('', $filename);
}
}
});

// Dynamically create a CMS page that is available via the Controller::PREVIEW_URL.
Event::listen('cms.router.beforeRoute', function ($url) {
// Ignore the route prefix if it is set.
$site = Site::getSiteFromContext();

if ($site->is_prefixed) {
$url = str_replace($site->route_prefix, '', $url);
}

if (str_contains($url, Controller::PREVIEW_URL) && Backend\Facades\BackendAuth::getUser()) {
return Controller::instance()->getPreviewPage($url);
}

return Controller::instance()->getCmsPageForUrl($url);
});

// If the visited page is a boxes page, replace the page content with the rendered boxes.
Event::listen('cms.page.beforeRenderPage', function (CmsController $controller, $page) {
$isBoxesPage = isset($page->apiBag[CmsPageParams::BOXES_PAGE_ID]);

// If the current page is a boxes page, return the rendered content.
if ($isBoxesPage) {
$isEditor = isset($page->apiBag[CmsPageParams::BOXES_IS_EDITOR]) || $this->isEditModeRequest();

return $controller->renderComponent($isEditor ? 'boxesPageEditor' : 'boxesPage');
}

return '';
});

// Add the BoxList component to any page that serves a boxes page.
Event::listen('cms.page.init', function (CmsController $controller) {
$page = $controller->getPage();

if (isset($page->apiBag[CmsPageParams::BOXES_PAGE_ID])) {
$isEditor = isset($page->apiBag[CmsPageParams::BOXES_IS_EDITOR]) || $this->isEditModeRequest();

$controller->addComponent(
$isEditor ? BoxesPageEditor::class : BoxesPage::class,
$isEditor ? 'boxesPageEditor' : 'boxesPage',
[
'id' => $page->apiBag[CmsPageParams::BOXES_PAGE_ID] ?? 0,
'modelType' => $page->apiBag[CmsPageParams::BOXES_MODEL_TYPE] ?? Page::class,
],
true
);
}
});

$this->registerPageFinder();

// OFFLINE.SiteSearch
Event::listen('offline.sitesearch.extend', fn () => new SiteSearch());

// Multi-site support
Event::listen('cms.sitePicker.overridePattern', function ($page, $pattern, $currentSite, $proposedSite) {
if (!isset($page->apiBag[CmsPageParams::BOXES_PAGE_ID]) || $page->apiBag[CmsPageParams::BOXES_MODEL_TYPE] !== Page::class) {
return;
}

$boxesPage = Page::withoutGlobalScope(MultisiteScope::class)
->findOrFail(
$page->apiBag[CmsPageParams::BOXES_PAGE_ID]
);

return Cache::rememberForever(
Page::multisiteCacheKey($boxesPage->id, $proposedSite->id),
fn () => $boxesPage->findForSite($proposedSite->id)?->url,
);
});

// Add Seeder behavior to the models if the Seeder plugin is installed.
if (class_exists(\OFFLINE\Seeder\Behaviors\HasSeederFactoryBehavior::class)) {
Box::extend(static function ($model) {
$model->implement[] = \OFFLINE\Seeder\Behaviors\HasSeederFactoryBehavior::class;
});

if (class_exists(\OFFLINE\Boxes\Models\Content::class)) {
Content::extend(static function ($model) {
$model->implement[] = \OFFLINE\Seeder\Behaviors\HasSeederFactoryBehavior::class;
});
}
}

if (App::runningInBackend()) {
// Inject global CSS styles.
Block::set('head', '<link href="' . Url::to('plugins/offline/boxes/assets/css/offline.boxes.backend.css') . '" rel="stylesheet">');
}
}

/**
* Registers any front-end components implemented in this plugin.
*
* @return array
*/
public function registerComponents()
{
return [
BoxesPage::class => 'boxesPage',
BoxesPageEditor::class => 'boxesPageEditor',
];
}

public function registerFormWidgets()
{
return [
HiddenInput::class => 'hidden',
BoxesDataForm::class => 'boxesdataform',
BoxesEditor::class => 'boxes',
BoxFinder::class => 'boxfinder',
];
}



/**
* Registers backend navigation items for this plugin.
*
* @return array
*/
public function registerNavigation()
{
$label = BoxesSetting::get('main_menu_label');

if (!$label) {
$label = Lang::get('offline.boxes::lang.content');
}

$counter = 0;

if (Features::instance()->revisions && method_exists(Page::class, 'getUnpublishedDraftCount')) {
$counter = Page::getUnpublishedDraftCount();
}

return [
'boxes' => [
'label' => $label,
'url' => Backend::url('offline/boxes/editorcontroller'),
'iconSvg' => '/plugins/offline/boxes/assets/img/cube.svg',
'permissions' => ['offline.boxes.access_editor'],
'order' => Config::get('offline.boxes::config.main_menu_order', 500),
'counter' => $counter,
'counterLabel' => Lang::get('offline.boxes::lang.unpublished_changes'),
],
];
}

/**
* Register new Twig variables
* @return array
*/
public function registerMarkupTags()
{
return [
'filters' => [
'boxesPage' => [Page::class, 'getAttributeWhereSlug'],
],
];
}

public function registerPermissions()
{
return [
'offline.boxes.manage_settings' => [
'label' => trans('offline.boxes::lang.permissions.manage_settings'),
'tab' => 'Boxes',
],
'offline.boxes.access_editor' => [
'label' => trans('offline.boxes::lang.permissions.access_editor'),
'tab' => 'Boxes',
],
];
}

public function registerSettings()
{
return [
'boxes' => [
'label' => Lang::get('offline.boxes::lang.settings_label'),
'description' => Lang::get('offline.boxes::lang.settings_description'),
'category' => 'Boxes',
'icon' => 'icon-cube',
'class' => BoxesSetting::class,
'order' => 500,
'keywords' => 'boxes',
'permissions' => ['offline.boxes.manage_settings'],
],
];
}

protected function isEditModeRequest()
{
return get(Controller::PREVIEW_PARAM) && Backend\Facades\BackendAuth::getUser();
}

/**
* Add support for RainLab.Pages and the integrated Page Finder.
*/
protected function registerPageFinder(): void
{
$listTypes = function () {
return [
Page::MENU_TYPE_PAGES => 'OFFLINE.Boxes: ' . trans('offline.boxes::lang.pages'),
Page::MENU_TYPE_ALL_PAGES => 'OFFLINE.Boxes: ' . trans('offline.boxes::lang.all_pages'),
];
};

$getTypeInfo = function ($type) {
if ($type === Page::MENU_TYPE_PAGES) {
$refs = Page::query()
->with([
'children' => fn ($q) => $q->where('url', '<>', ''),
])
->get()
->pipe(fn ($pages) => new PatchedTreeCollection($pages))
->listsNested('name', 'slug', ' - ');

return [
'references' => $refs,
'nesting' => true,
'dynamicItems' => true,
];
}

if ($type === Page::MENU_TYPE_ALL_PAGES) {
return [
'nesting' => true,
'dynamicItems' => true,
];
}

return [];
};

$resolveItem = function ($type, $item, $url) {
if ($type === Page::MENU_TYPE_PAGES || $type === Page::MENU_TYPE_ALL_PAGES) {
return Page::resolveMenuItem($item, $url);
}

return null;
};

Event::listen('pages.menuitem.listTypes', $listTypes);
Event::listen('cms.pageLookup.listTypes', $listTypes);

Event::listen('pages.menuitem.getTypeInfo', $getTypeInfo);
Event::listen('cms.pageLookup.getTypeInfo', $getTypeInfo);

Event::listen('pages.menuitem.resolveItem', $resolveItem);
Event::listen('cms.pageLookup.resolveItem', $resolveItem);
}
}
Empty file removed README.md
Empty file.
17 changes: 17 additions & 0 deletions assets/css/offline.boxes.backend.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@


/** Style the Boxes counter less aggressive */
.mainmenu-item [data-menu-id="boxes"].counter {
background: rgba(255, 255, 255, .4);
padding: 2px 4px;
font-weight: bold;
color: white;
}

.navbar-mode-inline .mainmenu-item [data-menu-id="boxes"].counter {
position: absolute;
left: 12px;
top: auto;
bottom: 4px;
backdrop-filter: blur(4px);
}
Loading

0 comments on commit 4004551

Please sign in to comment.