3 * OpenEMR <http://open-emr.org>.
5 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
8 namespace OpenEMR\Core
;
10 use Symfony\Component\Yaml\Yaml
;
11 use Symfony\Component\Yaml\Exception\ParseException
;
16 * Helper class to generate some `<script>` and `<link>` elements based on a
17 * configuration file. This file would be a good place to include other helpers
18 * for creating a `<head>` element, but for now it sufficently handles the
23 * @author Robert Down <robertdown@live.com>
24 * @copyright Copyright (c) 2017 Robert Down
30 * Setup various <head> elements.
32 * See root_dir/config/config.yaml for available assets
34 * Example usage in a PHP view script:
36 * // Top of script with require_once statements
37 * use OpenEMR\Core\Header;
40 * // If no special assets are needed:
41 * Header::setupHeader();
43 * // If 1 special asset is needed:
44 * Header::setupHeader('key-of-asset');
46 * // If 2 or more assets are needed:
47 * Header::setupHeader(['array', 'of', 'keys']);
50 * Inside of a twig template (Parameters same as before):
52 * {{ includeAsset() }}
55 * Inside of a smarty template, use | (pipe) delimited string of key names
58 * {headerTemplate assets='key-of-asset'} (1 optional assets)
59 * {headerTemplate assets='array|of|keys'} (multiple optional assets. ie. via | delimiter)
62 * The above example will render `<script>` tags and `<link>` tag which
63 * bring in the requested assets from config.yaml
65 * @param array|string $assets Asset(s) to include
66 * @throws ParseException If unable to parse the config file
69 public static function setupHeader($assets = [])
73 echo self
::loadTheme();
74 echo self
::includeAsset($assets);
75 } catch (\InvalidArgumentException
$e) {
76 error_log($e->getMessage());
81 * Include an asset from a config file.
83 * Static function to read in a YAML file into an array, check if the
84 * $assets keys are in the config file, and from the config file generate
85 * the HTML for a `<script>` or `<link>` tag.
87 * This is a private function, use Header::setupHeader() instead
89 * @param array|string $assets Asset(s) to include
90 * @throws ParseException If unable to parse the config file
93 private static function includeAsset($assets = [])
96 if (is_string($assets)) {
100 // @TODO Hard coded the path to the config file, not good RD 2017-05-27
101 $map = self
::readConfigFile("{$GLOBALS['fileroot']}/config/config.yaml");
105 foreach ($map as $k => $opts) {
106 $autoload = (isset($opts['autoload'])) ?
$opts['autoload'] : false;
107 $rtl = (isset($opts['rtl'])) ?
$opts['rtl'] : false;
108 if ($autoload === true ||
in_array($k, $assets)) {
109 $tmp = self
::buildAsset($opts);
111 foreach ($tmp['scripts'] as $s) {
115 foreach ($tmp['links'] as $l) {
119 if ($rtl && $_SESSION['language_direction'] == 'rtl') {
120 $tmpRtl = self
::buildAsset($rtl);
121 foreach ($tmpRtl['scripts'] as $s) {
125 foreach ($tmpRtl['links'] as $l) {
132 $linksStr = implode("", $links);
133 $scriptsStr = implode("", $scripts);
134 return "\n{$linksStr}\n{$scriptsStr}\n";
138 * Build an html element from config options.
140 * @var array $opts Options
141 * @return array Array with `scripts` and `links` keys which contain arrays of elements
143 private static function buildAsset($opts = array())
145 $script = (isset($opts['script'])) ?
$opts['script'] : false;
146 $link = (isset($opts['link'])) ?
$opts['link'] : false;
147 $basePath = self
::parsePlaceholders($opts['basePath']);
153 $script = self
::parsePlaceholders($script);
154 $path = self
::createFullPath($basePath, $script);
155 $scripts[] = self
::createElement($path, 'script');
159 if (!is_string($link) && !is_array($link)) {
160 throw new \
InvalidArgumentException("Link must be of type string or array");
163 if (is_string($link)) {
167 foreach ($link as $l) {
168 $l = self
::parsePlaceholders($l);
169 $path = self
::createFullPath($basePath, $l);
170 $links[] = self
::createElement($path, 'link');
174 return ['scripts' => $scripts, 'links' => $links];
177 private static function loadTheme()
179 $link = '<link rel="stylesheet" href="%css_header%" type="text/css">';
180 return self
::parsePlaceholders($link);
184 * Parse a string for $GLOBAL key placeholders %key-name%.
186 * Perform a regex match all in the given subject for anything warpped in
187 * percent signs `%some-key%` and if that string exists in the $GLOBALS
188 * array, will replace the occurence with the value of that key.
190 * @param string $subject String containing placeholders (%key-name%)
191 * @return string The new string with properly replaced keys
193 public static function parsePlaceholders($subject)
197 preg_match_all($re, $subject, $matches, PREG_SET_ORDER
, 0);
199 foreach ($matches as $match) {
200 if (array_key_exists($match[1], $GLOBALS)) {
201 $subject = str_replace($match[0], $GLOBALS["{$match[1]}"], $subject);
209 * Create the actual HTML element.
211 * @param string $path File path to load
212 * @param string $type Must be `script` or `link`
213 * @return string mixed HTML element
215 private static function createElement($path, $type)
218 $script = "<script type=\"text/javascript\" src=\"%path%\"></script>\n";
219 $link = "<link rel=\"stylesheet\" href=\"%path%\" type=\"text/css\">\n";
221 $template = ($type == 'script') ?
$script : $link;
222 $v = $GLOBALS['v_js_includes'];
223 $path = $path . "?v={$v}";
224 return str_replace("%path%", $path, $template);
228 * Create a full path from given parts.
230 * @param string $base Base path
231 * @param string $path specific path / filename
232 * @return string The full path
234 private static function createFullPath($base, $path)
236 return $base . $path;
240 * Read a config file and turn it into an array.
242 * @param string $file Full path to filename
243 * @return array Array of assets
245 private static function readConfigFile($file)
248 $config = Yaml
::parse(file_get_contents($file));
249 return $config['assets'];
250 } catch (ParseException
$e) {
251 error_log($e->getMessage());
252 // @TODO need to handle this better. RD 2017-05-24