Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Civi/Core/Themes.php
Original file line number Diff line number Diff line change
Expand Up @@ -265,4 +265,11 @@ public function cssId($cssExt, $cssFile) {
return ($cssExt === 'civicrm') ? $cssFile : "$cssExt-$cssFile";
}

/**
* Clear the cache
*/
public function clearCache() {
$this->cache->clear();
}

}
21 changes: 21 additions & 0 deletions ext/riverlea/CRM/riverlea/BAO/RiverleaStream.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

use CRM_riverlea_ExtensionUtil as E;
use Civi\Core\HookInterface;
use Civi\Core\Event\PreEvent;
use Civi\Core\Event\PostEvent;

class CRM_riverlea_BAO_RiverleaStream extends CRM_riverlea_DAO_RiverleaStream implements HookInterface {

public static function self_hook_civicrm_pre(PreEvent $event): void {
// munge label for default name
if ($event->action === 'create' && !$event->params['name']) {
$event->params['name'] = \CRM_Utils_String::munge($event->params['label']);
}
}

public static function self_hook_civicrm_postCommit(PostEvent $event): void {
\Civi::service('themes')->clear();
}

}
20 changes: 20 additions & 0 deletions ext/riverlea/CRM/riverlea/DAO/RiverleaStream.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

/**
* DAOs provide an OOP-style facade for reading and writing database records.
*
* DAOs are a primary source for metadata in older versions of CiviCRM (<5.74)
* and are required for some subsystems (such as APIv3).
*
* This stub provides compatibility. It is not intended to be modified in a
* substantive way. Property annotations may be added, but are not required.
*/
class CRM_riverlea_DAO_RiverleaStream extends CRM_riverlea_DAO_Base {

/**
* Required by older versions of CiviCRM (<5.74).
* @var string
*/
public static $_tableName = 'civicrm_riverlea_stream';

}
14 changes: 14 additions & 0 deletions ext/riverlea/CRM/riverlea/Upgrader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php
use CRM_riverlea_ExtensionUtil as E;

/**
* Collection of upgrade steps.
*/
class CRM_riverlea_Upgrader extends \CRM_Extension_Upgrader_Base {

public function upgrade_1000(): bool {
E::schema()->createEntityTable('schema/upgrader/1000-RiverleaStream.entityType.php');
return TRUE;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

/*
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC. All rights reserved. |
| |
| This work is published under the GNU AGPLv3 license with some |
| permitted exceptions and without any warranty. For full license |
| and copyright information, see https://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/

namespace Civi\Api4\Action\RiverleaStream;

/**
* @inheritDoc
*
* This provides an API for getting the contents of stream css_files - which
* is useful for the previewer
*/
class GetWithFileContent extends \Civi\Api4\Generic\BasicBatchAction {

/**
* @inheritdoc
*/
protected function getSelect(): array {
return [
'name', 'label', 'description', 'is_reserved',
'extension', 'file_prefix',
'css_file', 'css_file_dark',
'vars', 'vars_dark',
'custom_css', 'custom_css_dark',
];
}

/**
* @inheritdoc
*/
protected function doTask($stream): array {
$stream['css_file_content'] = self::getFileContent($stream['css_file'], $stream['extension'], $stream['file_prefix'] ?? NULL);
$stream['css_file_dark_content'] = self::getFileContent($stream['css_file_dark'], $stream['extension'], $stream['file_prefix'] ?? NULL);
return $stream;
}

/**
* @return string
*/
public static function getFileContent(?string $path, ?string $extension, ?string $filePrefix): string {
if (!$path || !$extension) {
return '';
}
if ($filePrefix) {
$path = $filePrefix . '/' . $path;
}

// fix extra slashes
$path = str_replace('//', '/', $path);
// guard against path traversal
$path = str_replace('..', '', $path);

// get the full path based on the stream extension
$finalPath = \Civi::resources()->getPath($extension, $path);

if (is_file($finalPath)) {
// File exists and is a file? Return it!
return file_get_contents($finalPath) ?? '';
}
return '';
}

}
162 changes: 162 additions & 0 deletions ext/riverlea/Civi/Api4/Action/RiverleaStream/Render.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<?php

/*
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC. All rights reserved. |
| |
| This work is published under the GNU AGPLv3 license with some |
| permitted exceptions and without any warranty. For full license |
| and copyright information, see https://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/

namespace Civi\Api4\Action\RiverleaStream;

/**
* @inheritDoc
*/
class Render extends \Civi\Api4\Generic\BasicBatchAction {

/**
* @var bool
*
*/
protected bool $isFrontend = FALSE;

/**
* @var string
*
* How to handle dark mode when rendering
*
* By default this will be set based on isFrontend and the stream/global
* dark mode settings
*
* However it can set explicitly to 'dark', 'light', 'inherit' when previewing
*/
protected string $darkMode = '';

/**
* @inheritdoc
*/
protected function getSelect(): array {
return [
'name', 'label',
'extension', 'file_prefix',
'css_file', 'css_file_dark',
'vars', 'vars_dark',
'custom_css', 'custom_css_dark',
'dark_frontend', 'dark_backend',
];
}

/**
* @inheritdoc
*
* Compile the dynamic css for a stream
*/
protected function doTask($stream): array {
$content = [];

// if dark mode is not set explicitly, we derive it
// based on frontend/backend settings, stream level settings, global dark mode settings
if (!$this->darkMode) {
$this->darkMode = $this->getDarkModeDefault($stream);
}

$content[] = self::concatStreamCss($stream);

switch ($this->darkMode ?? NULL) {
case 'light':
// tell OS we want light for system elements
$content[] = ":root { color-scheme: light; }";
break;

case 'dark':
// tell OS we want dark for system elements
$content[] = ":root { color-scheme: dark; }";
// add stream dark unconditionally
$content[] = self::concatStreamCss($stream, TRUE);
break;

case 'inherit':
default:
// tell OS we are happy with light or dark for system elements
$content[] = ":root { color-scheme: light dark; }";
// add stream dark vars wrapped inside a media query
$content[] = '@media (prefers-color-scheme: dark) {';
$content[] = self::concatStreamCss($stream, TRUE);
$content[] = '}';
break;
}

return [
'content' => implode("\n", $content),
];
}

private function getDarkModeDefault(array $stream) {
if ($this->isFrontend) {
return $stream['dark_frontend'] ?? \Civi::settings()->get('riverlea_dark_mode_frontend');
}
else {
return $stream['dark_backend'] ?? \Civi::settings()->get('riverlea_dark_mode_backend');
}
}

/**
* This replaces stream meta with its dark version, ready to pass
* to concatStreamCss / getStreamCssFromFile
*/
private static function getDarkStream(array $stream): array {
$stream['label'] = $stream['label'] . " Dark Styles";
$stream['vars'] = $stream['vars_dark'];
$stream['css_file'] = $stream['css_file_dark'];
$stream['custom_css'] = $stream['custom_css_dark'];

return $stream;
}

/**
* @param array $stream the stream meta
* @param bool $darkMode whether to use the regular stream meta, or dark version
*
* @return string
*/
private static function concatStreamCss(array $stream, bool $darkMode = FALSE): string {
if ($darkMode) {
$stream = self::getDarkStream($stream);
}

$content = [];

if ($stream['css_file']) {
$content[] = "/* {$stream['label']} file: {$stream['css_file']} */";
$content[] = self::getStreamCssFromFile($stream);
}

if ($stream['vars']) {
$content[] = "/* {$stream['label']} vars */";

$content[] = ":root {";
foreach ($stream['vars'] as $var => $value) {
$content[] = "{$var}: {$value};";
}
$content[] = "}";
}

if ($stream['custom_css']) {
$content[] = "/* {$stream['label']} custom css */";
$content[] = $stream['custom_css'];
}

return implode("\n", $content);
}

/**
* @return string
*/
private static function getStreamCssFromFile(array $stream): string {
return GetWithFileContent::getFileContent($stream['css_file'], $stream['extension'], $stream['file_prefix']);
}

}
15 changes: 15 additions & 0 deletions ext/riverlea/Civi/Api4/RiverleaStream.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php
namespace Civi\Api4;

/**
* RiverleaStream entity.
*
* Provided by the RiverLea CiviCRM Theme Framework extension.
*
* @package Civi\Api4
*/
class RiverleaStream extends Generic\DAOEntity {

use Generic\Traits\ManagedEntity;

}
Loading