New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PWA manifest and new @ini_theme_color replacement #2241
Changes from 7 commits
5e0255e
ff1e4ea
fb1f908
6f85823
10b4c84
c907183
ac6ceee
2d8226d
b52493c
40ca854
6c4fa3b
f3d2b6a
dceb2cc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"display": "standalone" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
<?php | ||
|
||
namespace dokuwiki; | ||
|
||
class Manifest | ||
{ | ||
public function sendManifest() | ||
{ | ||
$manifest = retrieveConfig('manifest', 'jsonToArray'); | ||
|
||
global $conf; | ||
|
||
$manifest['scope'] = DOKU_REL; | ||
|
||
if (empty($manifest['name'])) { | ||
$manifest['name'] = $conf['title']; | ||
} | ||
|
||
if (empty($manifest['short_name'])) { | ||
$manifest['short_name'] = $conf['title']; | ||
} | ||
|
||
if (empty($manifest['description'])) { | ||
$manifest['description'] = $conf['tagline']; | ||
} | ||
|
||
if (empty($manifest['start_url'])) { | ||
$manifest['start_url'] = DOKU_REL; | ||
} | ||
|
||
$styleUtil = new \dokuwiki\StyleUtils(); | ||
$styleIni = $styleUtil->cssStyleini($conf['template']); | ||
$replacements = $styleIni['replacements']; | ||
|
||
if (empty($manifest['background_color'])) { | ||
$manifest['background_color'] = $replacements['__background__']; | ||
} | ||
|
||
if (empty($manifest['theme_color'])) { | ||
$manifest['theme_color'] = !empty($replacements['__theme_color__']) ? $replacements['__theme_color__'] : $replacements['__background_alt__']; | ||
} | ||
|
||
if (empty($manifest['icons'])) { | ||
$manifest['icons'] = []; | ||
$look = [ | ||
':wiki:logo.png', | ||
':logo.png', | ||
'images/logo.png', | ||
':wiki:apple-touch-icon.png', | ||
':apple-touch-icon.png', | ||
'images/apple-touch-icon.png', | ||
':wiki:favicon.svg', | ||
':favicon.svg', | ||
'images/favicon.svg', | ||
':wiki:favicon.ico', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. are .ico files supported? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't read the spec yet, but MDN has them in their example: https://developer.mozilla.org/en-US/docs/Web/Manifest#icons |
||
':favicon.ico', | ||
'images/favicon.ico', | ||
':wiki:logo', | ||
]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're basically adding all of these if we find them, right? It would be nice to fall back to the DokuWiki logo if none could be found. |
||
|
||
$abs = true; | ||
foreach ($look as $img) { | ||
if ($img[0] === ':') { | ||
$file = mediaFN($img); | ||
$ismedia = true; | ||
} else { | ||
$file = tpl_incdir() . $img; | ||
$ismedia = false; | ||
} | ||
|
||
if (file_exists($file)) { | ||
$imginfo = getimagesize($file); | ||
if ($ismedia) { | ||
$url = ml($img, '', true, '', $abs); | ||
} else { | ||
$url = tpl_basedir() . $img; | ||
if ($abs) { | ||
$url = DOKU_URL . substr($url, strlen(DOKU_REL)); | ||
} | ||
} | ||
$manifest['icons'][] = [ | ||
'src' => $url, | ||
'sizes' => $imginfo[0] . 'x' . $imginfo[1], | ||
'type' => $imginfo['mime'], | ||
]; | ||
}; | ||
} | ||
} | ||
|
||
trigger_event('MANIFEST_SEND', $manifest); | ||
|
||
header('Content-Type: application/manifest+json'); | ||
echo json_encode($manifest); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
<?php | ||
|
||
namespace dokuwiki; | ||
|
||
class StyleUtils | ||
{ | ||
/** | ||
* Load style ini contents | ||
* | ||
* Loads and merges style.ini files from template and config and prepares | ||
* the stylesheet modes | ||
* | ||
* @author Andreas Gohr <andi@splitbrain.org> | ||
* | ||
* @param string $tpl the used template | ||
* @param bool $preview load preview replacements | ||
* @return array with keys 'stylesheets' and 'replacements' | ||
*/ | ||
public function cssStyleini($tpl, $preview=false) { | ||
global $conf; | ||
|
||
$stylesheets = array(); // mode, file => base | ||
// guaranteed placeholder => value | ||
$replacements = array( | ||
'__text__' => "#000", | ||
'__background__' => "#fff", | ||
'__text_alt__' => "#999", | ||
'__background_alt__' => "#eee", | ||
'__text_neu__' => "#666", | ||
'__background_neu__' => "#ddd", | ||
'__border__' => "#ccc", | ||
'__highlight__' => "#ff9", | ||
'__link__' => "#00f", | ||
); | ||
|
||
// load template's style.ini | ||
$incbase = tpl_incdir($tpl); | ||
$webbase = tpl_basedir($tpl); | ||
$ini = $incbase.'style.ini'; | ||
if(file_exists($ini)){ | ||
$data = parse_ini_file($ini, true); | ||
|
||
// stylesheets | ||
if(is_array($data['stylesheets'])) foreach($data['stylesheets'] as $file => $mode){ | ||
$stylesheets[$mode][$incbase.$file] = $webbase; | ||
} | ||
|
||
// replacements | ||
if(is_array($data['replacements'])){ | ||
$replacements = array_merge($replacements, $this->cssFixreplacementurls($data['replacements'],$webbase)); | ||
} | ||
} | ||
|
||
// load configs's style.ini | ||
$webbase = DOKU_BASE; | ||
$ini = DOKU_CONF."tpl/$tpl/style.ini"; | ||
$incbase = dirname($ini).'/'; | ||
if(file_exists($ini)){ | ||
$data = parse_ini_file($ini, true); | ||
|
||
// stylesheets | ||
if(isset($data['stylesheets']) && is_array($data['stylesheets'])) foreach($data['stylesheets'] as $file => $mode){ | ||
$stylesheets[$mode][$incbase.$file] = $webbase; | ||
} | ||
|
||
// replacements | ||
if(isset($data['replacements']) && is_array($data['replacements'])){ | ||
$replacements = array_merge($replacements, $this->cssFixreplacementurls($data['replacements'],$webbase)); | ||
} | ||
} | ||
|
||
// allow replacement overwrites in preview mode | ||
if($preview) { | ||
$webbase = DOKU_BASE; | ||
$ini = $conf['cachedir'].'/preview.ini'; | ||
if(file_exists($ini)) { | ||
$data = parse_ini_file($ini, true); | ||
// replacements | ||
if(is_array($data['replacements'])) { | ||
$replacements = array_merge($replacements, $this->cssFixreplacementurls($data['replacements'], $webbase)); | ||
} | ||
} | ||
} | ||
|
||
return array( | ||
'stylesheets' => $stylesheets, | ||
'replacements' => $replacements | ||
); | ||
} | ||
|
||
|
||
/** | ||
* Amend paths used in replacement relative urls, refer FS#2879 | ||
* | ||
* @author Chris Smith <chris@jalakai.co.uk> | ||
* | ||
* @param array $replacements with key-value pairs | ||
* @param string $location | ||
* @return array | ||
*/ | ||
protected function cssFixreplacementurls($replacements, $location) { | ||
foreach($replacements as $key => $value) { | ||
$replacements[$key] = preg_replace('#(url\([ \'"]*)(?!/|data:|http://|https://| |\'|")#','\\1'.$location,$value); | ||
} | ||
return $replacements; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -255,6 +255,63 @@ function confToHash($file,$lower=false) { | |
return linesToHash($lines, $lower); | ||
} | ||
|
||
/** | ||
* Read a json config file into an array | ||
* | ||
* @param string $file | ||
* @return array | ||
*/ | ||
function jsonToArray($file) | ||
{ | ||
$json = file_get_contents($file); | ||
|
||
$conf = json_decode($json, true); | ||
|
||
$jsonError = json_last_error(); | ||
if (!is_array($conf) && $jsonError !== JSON_ERROR_NONE) { | ||
|
||
switch ($jsonError) { | ||
case JSON_ERROR_DEPTH: | ||
$jsonErrorText = 'The maximum stack depth has been exceeded'; | ||
break; | ||
case JSON_ERROR_STATE_MISMATCH: | ||
$jsonErrorText = 'Invalid or malformed JSON'; | ||
break; | ||
case JSON_ERROR_CTRL_CHAR: | ||
$jsonErrorText = 'Control character error, possibly incorrectly encoded'; | ||
break; | ||
case JSON_ERROR_SYNTAX: | ||
$jsonErrorText = 'Syntax error'; | ||
break; | ||
case JSON_ERROR_UTF8: | ||
$jsonErrorText = 'Malformed UTF-8 characters, possibly incorrectly encoded'; | ||
break; | ||
case JSON_ERROR_RECURSION: | ||
$jsonErrorText = 'One or more recursive references in the value to be encoded'; | ||
break; | ||
case JSON_ERROR_INF_OR_NAN: | ||
$jsonErrorText = 'One or more NAN or INF values in the value to be encoded'; | ||
break; | ||
case JSON_ERROR_UNSUPPORTED_TYPE: | ||
$jsonErrorText = 'A value of a type that cannot be encoded was given'; | ||
break; | ||
case JSON_ERROR_INVALID_PROPERTY_NAME: | ||
$jsonErrorText = 'A property name that cannot be encoded was given'; | ||
break; | ||
case JSON_ERROR_UTF16: | ||
$jsonErrorText = 'Malformed UTF-16 characters, possibly incorrectly encoded'; | ||
break; | ||
default: | ||
$jsonErrorText = 'Unknown Error Code'; | ||
} | ||
|
||
trigger_error('JSON decoding error "' . $jsonErrorText . '" for file ' . $file, E_USER_WARNING); | ||
return []; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This error handling seems excessive. First of all it could probably be better handled with all the error codes in a lookup array (with the JSON* constants as the key). Then I'm wondering about using trigger_error(). Have you tried what happens when you pass invalid json to |
||
} | ||
|
||
return $conf; | ||
} | ||
|
||
/** | ||
* Retrieve the requested configuration information | ||
* | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -82,6 +82,7 @@ function load_autoload($name){ | |
'RemoteAPI' => DOKU_INC.'inc/remote.php', | ||
'RemoteAPICore' => DOKU_INC.'inc/RemoteAPICore.php', | ||
'Subscription' => DOKU_INC.'inc/subscription.php', | ||
'StyleUtil' => DOKU_INC.'inc/StyleUtil.php', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This shouldn't be needed because the file is namespaced and the autoloader should take care of this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also the class seems to be named |
||
|
||
'DokuWiki_Action_Plugin' => DOKU_PLUGIN.'action.php', | ||
'DokuWiki_Admin_Plugin' => DOKU_PLUGIN.'admin.php', | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what does this refer to? There is no
images
folder in dokuwiki. There might be one in the template but you wouldn't want that one if you already found one inwiki:logo.png
?