e107_handlers/library_manager.php
<?php
/**
* e107 website system
*
* Copyright (C) 2008-2017 e107 Inc (e107.org)
* Released under the terms and conditions of the
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
*
* @file
* External library handling for e107 core/plugins/themes.
*
* TODO:
* - Provide the ability to use third-party callbacks (are defined in e_library.php files) for groups:
* 'info', 'pre_detect', 'post_detect', 'pre_dependencies_load', 'pre_load', 'post_load'
*/
// [e_LANGUAGEDIR]/[e_LANGUAGE]/lan_library_manager.php
e107::lan('core', 'library_manager');
/**
* Class core_library.
*/
class core_library
{
/**
* Provides information about external libraries.
*
* Provides information about:
* - jQuery (CDN).
* - jQuery (local).
* - jQuery Once (CDN)
* - jQuery Once (local)
* - jQuery UI (CDN)
* - jQuery UI (local)
* - Bootstrap (CDN)
* - Bootstrap (local)
* - Bootstrap Editable (CDN)
* - Bootstrap Editable (local)
* - Font-Awesome (CDN)
* - Font-Awesome (local)
*/
public function config()
{
$libraries = array();
// jQuery (CDN).
$libraries['cdn.jquery'] = array(
'name' => 'jQuery 2 (CDN)',
'vendor_url' => 'https://jquery.com/',
'version_arguments' => array(
'file' => 'jquery.min.js',
'pattern' => '/jQuery\s+v(\d\.\d\.\d+)/',
'lines' => 5,
),
'files' => array(
'js' => array(
'jquery.min.js' => array(
'zone' => 1,
'type' => 'url',
),
),
),
'variants' => array(
// 'unminified' version for debugging.
'dev' => array(
'files' => array(
'js' => array(
'jquery.js' => array(
'zone' => 1,
'type' => 'url',
),
),
),
),
),
// Override library path to CDN.
'library_path' => 'https://cdn.jsdelivr.net/jquery',
'path' => '2.2.4',
'version' => '2.2.4',
);
// jQuery (local).
$libraries['jquery'] = array(
'name' => 'jQuery 2 (local)',
'vendor_url' => 'https://jquery.com/',
'version_arguments' => array(
'file' => 'dist/jquery.min.js',
'pattern' => '/v(\d\.\d\.\d+)/',
'lines' => 5,
),
'files' => array(
'js' => array(
'dist/jquery.min.js' => array(
'zone' => 1,
'type' => 'url',
),
),
),
'variants' => array(
// 'unminified' version for debugging.
'dev' => array(
'files' => array(
'js' => array(
'dist/jquery.js' => array(
'zone' => 1,
'type' => 'url',
),
),
),
),
),
'library_path' => '{e_WEB}lib/jquery',
'path' => '2.2.4',
'version' => '2.2.4',
);
// jQuery Once (CDN).
$libraries['cdn.jquery.once'] = array(
'name' => 'jQuery Once (CDN)',
'vendor_url' => 'https://plugins.jquery.com/once/',
'version_arguments' => array(
'file' => 'jquery.once.min.js',
'pattern' => '/jQuery\sOnce\s+v(\d\.\d\.\d+)/',
'lines' => 5,
),
'files' => array(
'js' => array(
'jquery.once.min.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
),
'variants' => array(
// 'unminified' version for debugging.
'dev' => array(
'files' => array(
'js' => array(
// There is no non-minified version.
'jquery.once.min.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
),
),
),
// Override library path to CDN.
'library_path' => 'https://cdn.jsdelivr.net/jquery.once',
'path' => '2.1.2',
'version' => '2.1.2',
);
// jQuery Once (local).
$libraries['jquery.once'] = array(
'name' => 'jQuery Once (local)',
'vendor_url' => 'https://plugins.jquery.com/once/',
'version_arguments' => array(
'file' => 'jquery.once.min.js',
'pattern' => '/jQuery\sOnce\s+v(\d\.\d\.\d+)/',
'lines' => 5,
),
'files' => array(
'js' => array(
'jquery.once.min.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
),
'variants' => array(
// 'unminified' version for debugging.
'dev' => array(
'files' => array(
'js' => array(
// There is no non-minified version.
'jquery.once.min.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
),
),
),
// Override library path.
'library_path' => '{e_WEB}lib/jquery-once',
'version' => '2.2.3',
'path' => '',
);
// jQuery UI (CDN).
$libraries['cdn.jquery.ui'] = array(
'name' => 'jQuery UI (CDN)',
'vendor_url' => 'https://jqueryui.com/',
'version_arguments' => array(
'file' => 'jquery-ui.min.js',
'pattern' => '/v(\d\.\d+\.\d+)/',
'lines' => 5,
),
'files' => array(
'js' => array(
'jquery-ui.min.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
'css' => array(
'themes/base/jquery-ui.min.css' => array(
'zone' => 2,
),
),
),
'variants' => array(
// 'unminified' version for debugging.
'dev' => array(
'files' => array(
'js' => array(
// There is no non-minified version.
'jquery-ui.min.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
'css' => array(
// There is no non-minified version.
'jquery-ui.min.css' => array(
'zone' => 2,
),
),
),
),
),
// Override library path to CDN.
'library_path' => 'https://cdnjs.cloudflare.com/ajax/libs/jqueryui',
'path' => '1.13.2',
'version' => '1.13.2',
);
// jQuery UI (local).
$libraries['jquery.ui'] = array(
'name' => 'jQuery UI (local)',
'vendor_url' => 'https://jqueryui.com/',
'version_arguments' => array(
'file' => 'jquery-ui.js',
'pattern' => '/v(\d\.\d+\.\d+)/',
'lines' => 5,
),
'files' => array(
'js' => array(
'jquery-ui.min.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
'css' => array(
'jquery-ui.min.css' => array(
'zone' => 2,
),
),
),
'variants' => array(
// 'unminified' version for debugging.
'dev' => array(
'files' => array(
'js' => array(
'jquery-ui.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
'css' => array(
'jquery-ui.css' => array(
'zone' => 2,
),
),
),
),
),
// Override library path.
'library_path' => '{e_WEB}lib/jquery-ui',
'version' => '1.13.2'
);
// ----------------- jQuery 3 (frontend only) --------------//
// jQuery (local).
$libraries['jquery3'] = array(
'name' => 'jQuery 3 (local)',
'vendor_url' => 'https://jquery.com/',
'version_arguments' => array(
'file' => 'jquery.min.js',
'pattern' => '/v(\d\.\d\.\d+)/',
'lines' => 5,
),
'files' => array(
'js' => array(
'jquery.min.js' => array(
'zone' => 1,
'type' => 'url',
),
),
),
'variants' => array(
),
'library_path' => '{e_WEB}lib/jquery',
'path' => '3',
'version' => '3.7.1',
);
// https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js
https://cdn.jsdelivr.net/jquery/npm/jquery@3.6.0/dist/jquery.min.js
$libraries['cdn.jquery3'] = array(
'name' => 'jQuery 3 (CDN)',
'vendor_url' => 'https://jquery.com/',
'version_arguments' => array(
'file' => 'jquery.min.js',
'pattern' => '/jQuery\s+v(\d\.\d\.\d+)/',
'lines' => 5,
),
'files' => array(
'js' => array(
'jquery.min.js' => array(
'zone' => 1,
'type' => 'url',
),
),
),
'variants' => array(
),
// Override library path to CDN.
'library_path' => 'https://cdnjs.cloudflare.com/ajax/libs/jquery',
'path' => '3.7.1',
'version' => '3.7.1',
);
// ----------------- Bootstrap 5 ---------------------------//
// Bootstrap (CDN).
$libraries['cdn.bootstrap5'] = array(
'name' => 'Bootstrap 5 (CDN)',
'vendor_url' => 'http://getbootstrap.com/',
'version_arguments' => array(
'file' => 'dist/js/bootstrap.min.js',
'pattern' => '/Bootstrap\s+v(\d\.\d\.\d+)/',
'lines' => 5,
),
'files' => array(
'js' => array(
'dist/js/bootstrap.bundle.min.js' => array(
'zone' => 2,
'type' => 'footer',
'defer' => true,
),
),
'css' => array(
'dist/css/bootstrap.min.css' => array(
'zone' => 1,
),
),
),
'variants' => array(),
// Override library path to CDN.
//https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css
'library_path' => 'https://cdn.jsdelivr.net/npm/bootstrap@5.2.3',
'path' => '',
'version' => '5.2.3',
);
// Bootstrap (local).
$libraries['bootstrap5'] = array(
'name' => 'Bootstrap 5 (local)',
'vendor_url' => 'http://getbootstrap.com/',
'version_arguments' => array(
'file' => 'js/bootstrap.bundle.min.js',
'pattern' => '/Bootstrap\s+v(\d\.\d\.\d+)/',
'lines' => 5,
),
'files' => array(
'js' => array(
'js/bootstrap.bundle.min.js' => array(
'zone' => 2,
'type' => 'footer',
'defer' => true,
),
),
'css' => array(
'css/bootstrap.min.css' => array(
'zone' => 2,
),
),
),
'variants' => array(),
'library_path' => '{e_WEB}lib/bootstrap',
'path' => '5',
'version' => '5.2.3',
);
// ----------------- Bootstrap 4 ---------------------------//
// Bootstrap (CDN).
$libraries['cdn.bootstrap4'] = array(
'name' => 'Bootstrap 4 (CDN)',
'vendor_url' => 'http://getbootstrap.com/',
'version_arguments' => array(
'file' => 'dist/js/bootstrap.min.js',
'pattern' => '/Bootstrap\s+v(\d\.\d\.\d+)/',
'lines' => 5,
),
'files' => array(
'js' => array(
'dist/js/bootstrap.bundle.min.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
'css' => array(
'dist/css/bootstrap.min.css' => array(
'zone' => 1,
),
),
),
'variants' => array(
// 'unminified' version for debugging.
/*'dev' => array(
'files' => array(
'js' => array(
'js/bootstrap.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
'css' => array(
'css/bootstrap.css' => array(
'zone' => 2,
),
),
),
),*/
),
// Override library path to CDN.
// https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/js/bootstrap.bundle.min.js
'library_path' => 'https://cdn.jsdelivr.net/npm/bootstrap@4.3.1',
'path' => '',
'version' => '4.3.1',
);
// Bootstrap (local).
$libraries['bootstrap4'] = array(
'name' => 'Bootstrap 4 (local)',
'vendor_url' => 'http://getbootstrap.com/',
'version_arguments' => array(
'file' => 'js/bootstrap.bundle.min.js',
'pattern' => '/Bootstrap\s+v(\d\.\d\.\d+)/',
'lines' => 5,
),
'files' => array(
'js' => array(
'js/bootstrap.bundle.min.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
'css' => array(
'css/bootstrap.min.css' => array(
'zone' => 2,
),
),
),
'variants' => array(
// 'unminified' version for debugging.
'dev' => array(
'files' => array(
'js' => array(
'js/bootstrap.bundle.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
'css' => array(
'css/bootstrap.css' => array(
'zone' => 2,
),
),
),
),
),
'library_path' => '{e_WEB}lib/bootstrap',
'path' => '4',
'version' => '4.3.1',
);
// ----------------------------------------------------- //
// Bootstrap (CDN).
$libraries['cdn.bootstrap'] = array(
'name' => 'Bootstrap (CDN)',
'vendor_url' => 'http://getbootstrap.com/',
'version_arguments' => array(
'file' => 'js/bootstrap.min.js',
'pattern' => '/Bootstrap\s+v(\d\.\d\.\d+)/',
'lines' => 5,
),
'files' => array(
'js' => array(
'js/bootstrap.min.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
'css' => array(
'css/bootstrap.min.css' => array(
'zone' => 2,
),
),
),
'variants' => array(
// 'unminified' version for debugging.
'dev' => array(
'files' => array(
'js' => array(
'js/bootstrap.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
'css' => array(
'css/bootstrap.css' => array(
'zone' => 2,
),
),
),
),
),
// Override library path to CDN.
// 'library_path' => 'https://cdn.jsdelivr.net/bootstrap',
'library_path' => 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap',
'path' => '3.4.1',
'version' => '3.4.1',
);
// Bootstrap (local).
$libraries['bootstrap'] = array(
'name' => 'Bootstrap (local)',
'vendor_url' => 'http://getbootstrap.com/',
'version_arguments' => array(
'file' => 'js/bootstrap.min.js',
'pattern' => '/Bootstrap\s+v(\d\.\d\.\d+)/',
'lines' => 5,
),
'files' => array(
'js' => array(
'js/bootstrap.min.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
'css' => array(
'css/bootstrap.min.css' => array(
'zone' => 2,
),
),
),
'variants' => array(
// 'unminified' version for debugging.
'dev' => array(
'files' => array(
'js' => array(
'js/bootstrap.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
'css' => array(
'css/bootstrap.css' => array(
'zone' => 2,
),
),
),
),
),
'library_path' => '{e_WEB}lib/bootstrap',
'path' => '3',
'version' => '3.4.1',
'preload' => array(
0 => array(
'as' => 'font',
'type' => 'font/woff2',
'path' => 'fonts/glyphicons-halflings-regular.woff2',
'crossorigin' => true,
'browsercache' => false,
),
),
);
// Bootstrap Editable (CDN).
$libraries['cdn.bootstrap.editable'] = array(
'name' => 'Bootstrap Editable (CDN)',
'vendor_url' => 'https://vitalets.github.io/bootstrap-editable/',
'version_arguments' => array(
'file' => 'js/bootstrap-editable.min.js',
'pattern' => '/v(\d\.\d\.\d+)/',
'lines' => 5,
),
'files' => array(
'js' => array(
'js/bootstrap-editable.min.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
'css' => array(
'css/bootstrap-editable.min.css' => array(
'zone' => 2,
),
),
),
'variants' => array(
// 'unminified' version for debugging.
'dev' => array(
'files' => array(
'js' => array(
'js/bootstrap-editable.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
'css' => array(
'css/bootstrap-editable.css' => array(
'zone' => 2,
),
),
),
),
),
// Override library path to CDN.
'library_path' => 'https://cdn.jsdelivr.net/bootstrap.editable',
'path' => '1.5.1',
'version' => '1.5.1',
);
// Bootstrap Editable (local).
$libraries['bootstrap.editable'] = array(
'name' => 'Bootstrap Editable (local)',
'vendor_url' => 'https://vitalets.github.io/bootstrap-editable/',
'version_arguments' => array(
'file' => 'js/bootstrap-editable.min.js',
'pattern' => '/v(\d\.\d\.\d+)/',
'lines' => 5,
),
'files' => array(
'js' => array(
'js/bootstrap-editable.min.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
'css' => array(
'css/bootstrap-editable.min.css' => array(
'zone' => 2,
),
),
),
'variants' => array(
// 'unminified' version for debugging.
'dev' => array(
'files' => array(
'js' => array(
'js/bootstrap-editable.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
'css' => array(
'css/bootstrap-editable.css' => array(
'zone' => 2,
),
),
),
),
),
// Override library path.
'library_path' => '{e_WEB}js/bootstrap3-editable',
'version' => '1.5.1',
'path' => '',
);
// Bootstrap Switch (CDN).
$libraries['cdn.bootstrap.switch'] = array(
'name' => 'Bootstrap Switch (CDN)',
'vendor_url' => 'http://www.bootstrap-switch.org',
'version_arguments' => array(
'file' => 'js/bootstrap-switch.min.js',
'pattern' => '/v(\d\.\d\.\d)/',
'lines' => 5,
),
'files' => array(
'js' => array(
'js/bootstrap-switch.min.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
'css' => array(
'css/bootstrap3/bootstrap-switch.min.css' => array(
'zone' => 2,
),
),
),
'variants' => array(
// 'unminified' version for debugging.
'dev' => array(
'files' => array(
'js' => array(
'js/bootstrap-switch.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
'css' => array(
'css/bootstrap3/bootstrap-switch.css' => array(
'zone' => 2,
),
),
),
),
),
// Override library path to CDN.
'library_path' => 'https://cdn.jsdelivr.net/bootstrap.switch',
'path' => '3.3.2',
'version' => '3.3.2',
);
// Bootstrap Switch (local).
$libraries['bootstrap.switch'] = array(
'name' => 'Bootstrap Switch (local)',
'vendor_url' => 'http://www.bootstrap-switch.org',
'version_arguments' => array(
'file' => 'dist/js/bootstrap-switch.min.js',
'pattern' => '/v(\d\.\d\.\d)/',
'lines' => 5,
),
'files' => array(
'js' => array(
'dist/js/bootstrap-switch.min.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
'css' => array(
'dist/css/bootstrap3/bootstrap-switch.min.css' => array(
'zone' => 2,
),
),
),
'variants' => array(
// 'unminified' version for debugging.
'dev' => array(
'files' => array(
'js' => array(
'dist/js/bootstrap-switch.js' => array(
'zone' => 2,
'type' => 'footer',
),
),
'css' => array(
'dist/css/bootstrap3/bootstrap-switch.css' => array(
'zone' => 2,
),
),
),
),
),
// Override library path.
'library_path' => '{e_WEB}lib/bootstrap-switch',
'version' => '3.3.2',
);
// Font-Awesome 4 (CDN).
$libraries['cdn.fontawesome'] = array(
'name' => 'Font-Awesome 4 (CDN)',
'vendor_url' => 'http://fontawesome.io/',
'version_arguments' => array(
'file' => 'css/font-awesome.min.css',
'pattern' => '/(\d\.\d\.\d+)/',
'lines' => 10,
),
'files' => array(
'css' => array(
'css/font-awesome.min.css' => array(
'zone' => 2,
),
),
),
'variants' => array(
// 'unminified' version for debugging.
'dev' => array(
'files' => array(
'css' => array(
'css/font-awesome.css' => array(
'zone' => 2,
),
),
),
),
),
// Override library path to CDN.
'library_path' => 'https://cdn.jsdelivr.net/fontawesome',
'path' => '4.7.0',
'version' => '4.7.0',
);
// Font-Awesome (local).
$libraries['fontawesome'] = array(
'name' => 'Font-Awesome 4 (local)',
'vendor_url' => 'http://fontawesome.io/',
'version_arguments' => array(
'file' => 'css/font-awesome.min.css',
'pattern' => '/(\d\.\d\.\d+)/',
'lines' => 10,
),
'files' => array(
'css' => array(
'css/font-awesome.min.css' => array(
'zone' => 2,
),
),
),
'variants' => array(
// 'unminified' version for debugging.
'dev' => array(
'files' => array(
'css' => array(
'css/font-awesome.css' => array(
'zone' => 2,
),
),
),
),
),
// Override library path.
'library_path' => '{e_WEB}lib/font-awesome',
'path' => '4.7.0',
'version' => '4.7.0',
// preload in header using <link> tag. for speed optimization.
'preload' => array(
0 => array(
'as' => 'style',
'type' => '',
'path' => 'css/font-awesome.min.css',
'crossorigin' => false,
'browsercache' => true, // add the generated ?xxxxxx to the path.
),
1 => array(
'as' => 'font',
'type' => 'font/woff2',
'path' => 'fonts/fontawesome-webfont.woff2?v=4.7.0',
'crossorigin' => true,
'browsercache' => false,
),
),
);
// Font-Awesome 5 (CDN).
$libraries['cdn.fontawesome5'] = array(
'name' => 'Font-Awesome 5 (CDN)',
'vendor_url' => 'https://fontawesome.com/',
'version_arguments' => array(
'file' => 'css/all.css',
'pattern' => '/(\d\.\d*\.\d+)/',
'lines' => 2,
),
'files' => array(
'js' => array(
'js/all.min.js' => array(
'zone' => 2,
'type' => 'footer',
'defer' => true,
),
'js/v4-shims.min.js' => array(
'zone' => 2,
'type' => 'footer',
'defer' => true,
),
),
'css' => array(
'css/all.min.css' => array(
'zone' => 2,
),
'css/v4-shims.min.css' => array(
'zone' => 2,
),
),
),
'variants' => array(),
// Override library path to CDN.
'library_path' => 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4',
'path' => '',
'version' => '5.15.4',
);
// Font-Awesome 5 (local).
$libraries['fontawesome5'] = array(
'name' => 'Font-Awesome 5 (local)',
'vendor_url' => 'https://fontawesome.com/',
'version_arguments' => array(
'file' => 'css/all.css',
'pattern' => '/(\d\.\d*\.\d+)/',
'lines' => 3,
),
'files' => array(
'js' => array(
'js/all.min.js' => array(
'zone' => 2,
'type' => 'footer',
'defer' => true,
),
'js/v4-shims.min.js' => array(
'zone' => 2,
'type' => 'footer',
'defer' => true,
),
),
'css' => array(
'css/all.min.css' => array(
'zone' => 2,
),
'css/v4-shims.min.css' => array(
'zone' => 2,
),
),
),
'variants' => array(),
'library_path' => '{e_WEB}lib/font-awesome',
'path' => '5',
'version' => '5.15.4',
/* 'preload' => array(
1 => array(
'as' => 'font',
'type' => 'font/woff',
'path' => 'webfonts/fa-solid-900.woff',
'crossorigin' => false,
'browsercache' => false,
),
2 => array(
'as' => 'font',
'type' => 'font/woff',
'path' => 'webfonts/fa-brands-400.woff',
'crossorigin' => false,
'browsercache' => false,
),
3 => array(
'as' => 'font',
'type' => 'font/woff2',
'path' => 'webfonts/fa-solid-900.woff2',
'crossorigin' => false,
'browsercache' => false,
),
4 => array(
'as' => 'font',
'type' => 'font/woff2',
'path' => 'webfonts/fa-brands-400.woff2',
'crossorigin' => false,
'browsercache' => false,
),
),*/
);
// Font-Awesome 6 (local).
$libraries['fontawesome6'] = array(
'name' => 'Font-Awesome 6 (local)',
'vendor_url' => 'https://fontawesome.com/',
'version_arguments' => array(
'file' => 'css/all.css',
'pattern' => '/(\d\.\d*\.\d+)/',
'lines' => 3,
),
'files' => array(
'js' => array(
'js/all.min.js' => array(
'zone' => 2,
'type' => 'footer',
'defer' => true,
),
'js/v4-shims.min.js' => array(
'zone' => 2,
'type' => 'footer',
'defer' => true,
),
),
'css' => array(
'css/all.min.css' => array(
'zone' => 2,
),
'css/v4-shims.min.css' => array(
'zone' => 2,
),
),
),
'variants' => array(),
'library_path' => '{e_WEB}lib/font-awesome',
'path' => '6',
'version' => '6.4.2',
);
// Font-Awesome 6 (CDN).
$libraries['cdn.fontawesome6'] = array(
'name' => 'Font-Awesome 6 (CDN)',
'vendor_url' => 'https://fontawesome.com/',
'version_arguments' => array(
'file' => 'css/all.css',
'pattern' => '/(\d\.\d*\.\d+)/',
'lines' => 2,
),
'files' => array(
'js' => array(
'js/all.min.js' => array(
'zone' => 2,
'type' => 'footer',
'defer' => true,
),
'js/v4-shims.min.js' => array(
'zone' => 2,
'type' => 'footer',
'defer' => true,
),
),
'css' => array(
'css/all.min.css' => array(
'zone' => 2,
),
'css/v4-shims.min.css' => array(
'zone' => 2,
),
),
),
'variants' => array(),
// Override library path to CDN.
'library_path' => 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2',
'path' => '',
'version' => '6.4.2',
);
// Animate (local).
$libraries['animate.css'] = array(
'name' => 'Animate.css (local)',
'vendor_url' => 'https://daneden.github.io/animate.css/',
'version_arguments' => array(
'file' => 'animate.min.css',
'pattern' => '/(\d\.\d\.\d+)/',
'lines' => 5,
),
'files' => array(
'css' => array(
'animate.min.css' => array(
'zone' => 2,
),
),
),
'library_path' => '{e_WEB}lib/animate.css',
// 'path' => '3.5.2',
'version' => '3.5.2',
);
// Animate (local).
$libraries['bootstrap-suggest'] = array(
'name' => 'Bootstrap Suggest (local)',
'vendor_url' => 'https://github.com/lodev09/bootstrap-suggest',
'version_arguments' => array(
'file' => 'bootstrap-suggest.js',
'pattern' => '/(\d\.\d\.\d+)/',
'lines' => 3,
),
'files' => array(
'css' => array(
'bootstrap-suggest.css' => array(
'zone' => 2,
),
),
'js' => array(
'bootstrap-suggest.min.js' => array(
'zone' => 2,
),
),
),
'library_path' => '{e_WEB}lib/bootstrap-suggest',
'path' => 'dist',
'version' => '2.0.3',
);
return $libraries;
}
/**
* Alters library information before detection and caching takes place.
* @param array $libraries
*/
function config_alter(&$libraries)
{
$pref = e107::pref('core');
$cdnProvider = varset($pref['e_jslib_cdn_provider'], 'jsdelivr');
// If CDNJS is the selected provider, we alter core CDN libraries to use it
// instead of jsDelivr.
if($cdnProvider == 'cdnjs')
{
$libraries['cdn.jquery']['library_path'] = str_replace('https://cdn.jsdelivr.net/jquery', 'https://cdnjs.cloudflare.com/ajax/libs/jquery', $libraries['cdn.jquery']['library_path']);
$libraries['cdn.jquery.once']['library_path'] = str_replace('https://cdn.jsdelivr.net/jquery.once', 'https://cdnjs.cloudflare.com/ajax/libs/jquery-once', $libraries['cdn.jquery.once']['library_path']);
$libraries['cdn.jquery.ui']['library_path'] = str_replace('https://cdn.jsdelivr.net/jquery.ui', 'https://cdnjs.cloudflare.com/ajax/libs/jqueryui', $libraries['cdn.jquery.ui']['library_path']);
$libraries['cdn.bootstrap']['library_path'] = str_replace('https://cdn.jsdelivr.net/bootstrap', 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap', $libraries['cdn.bootstrap']['library_path']);
$libraries['cdn.bootstrap.editable']['library_path'] = str_replace('https://cdn.jsdelivr.net/bootstrap.editable', 'https://cdnjs.cloudflare.com/ajax/libs/x-editable', $libraries['cdn.bootstrap.editable']['library_path']);
$libraries['cdn.bootstrap.editable']['path'] .= '/bootstrap-editable';
$libraries['cdn.bootstrap.switch']['library_path'] = str_replace('https://cdn.jsdelivr.net/bootstrap.switch', 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-switch', $libraries['cdn.bootstrap.switch']['library_path']);
$libraries['cdn.fontawesome']['library_path'] = str_replace('https://cdn.jsdelivr.net/fontawesome', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome', $libraries['cdn.fontawesome']['library_path']);
}
}
}
/**
* Class e_library_manager.
*/
class e_library_manager
{
/** @var array list of callbacks to track performance */
private $callbacks = array();
/**
* Constructor
* Use {@link getInstance()}, direct instantiating is not possible for signleton objects.
*/
public function __construct()
{
}
/**
* @return void
*/
protected function _init()
{
}
/**
* Cloning is not allowed.
*/
private function __clone()
{
}
/**
* @return array
*/
public function getCallbackLog()
{
return $this->callbacks;
}
/**
* Tries to detect a library and its installed version.
*
* @param $name
* The machine name of a library to return registered information for.
* @param bool $force - removes the 'version' and has the callback look it up from the library file.
* @return array|false
* An associative array containing registered information for the library specified by $name, or FALSE if the
* library $name is not registered. In addition to the keys returned by info(), the following keys are
* contained:
* - installed: A boolean indicating whether the library is installed. Note that not only the top-level library,
* but also each variant contains this key.
* - version: If the version could be detected, the full version string.
* - error: If an error occurred during library detection, one of the following error statuses:
* "not found", "not detected", "not supported".
* - error_message: If an error occurred during library detection, a detailed error_message.
*/
public function detect($name, $force = false)
{
// Re-use the statically cached value of info() to save memory.
$library = &$this->info($name, $force);
// Exit early if the library was not found.
if($library === false)
{
return $library;
}
// If 'installed' is set, library detection ran already.
if(isset($library['installed']))
{
return $library;
}
$library['installed'] = false;
// Check whether the library exists.
if(!isset($library['library_path']) && !empty($library['machine_name']))
{
$library['library_path'] = $this->detectPath($library['machine_name']);
}
$libraryPath = !empty($library['library_path']) ? e107::getParser()->replaceConstants($library['library_path']) : '';
if(empty($library['library_path']) || (!empty($libraryPath) && !file_exists($libraryPath) && strpos($libraryPath, 'http') !== 0))
{
$library['error'] = LAN_NOT_FOUND;
$replace_with = array($name);
$library['error_message'] = e107::getParser()->lanVars(LAN_LIBRARY_MANAGER_03, $replace_with, true);
return $library;
}
// TODO:
// Invoke callbacks in the 'pre_detect' group.
$this->invoke('pre_detect', $library);
if($force === true)
{
unset($library['version']);
}
// Detect library version, if not hardcoded.
if(!isset($library['version']))
{
// If version_callback is a method in $this class.
if(method_exists($this, $library['version_callback'])) // runs getVersion() which will be slow.
{
$this->callbacks[] = $name;
e107::getDebug()->logTime("[Looking up version of ".$name."]");
// We support both a single parameter, which is an associative array, and an indexed array of multiple
// parameters.
if(isset($library['version_arguments'][0]))
{
// Add the library as the first argument.
$classMethod = array($this, $library['version_callback']);
$params = array_merge(array($library), $library['version_arguments']);
$library['version'] = call_user_func_array($classMethod, $params);
}
else
{
$method = $library['version_callback'];
$library['version'] = $this->$method($library, $library['version_arguments']);
}
}
// If version_callback is a method in e_library.php file.
else
{
$library['version'] = '';
$class = false;
if(!empty($library['plugin']))
{
$class = e107::getAddon($library['plugin'], 'e_library');
}
elseif(!empty($library['theme']))
{
// e107::getAddon() does not support theme folders.
if(is_readable(e_THEME . $library['theme'] . '/theme_library.php'))
{
e107_require_once(e_THEME . $library['theme'] . '/theme_library.php');
$addonClass = 'theme_library';
if(class_exists($addonClass))
{
$class = new $addonClass();
}
}
// e107::getAddon() does not support theme folders.
if(is_readable(e_THEME . $library['theme'] . '/admin_theme_library.php'))
{
e107_require_once(e_THEME . $library['theme'] . '/admin_theme_library.php');
$addonClass = 'admin_theme_library';
if(class_exists($addonClass))
{
$class = new $addonClass();
}
}
}
// We support both a single parameter, which is an associative array, and an
// indexed array of multiple parameters.
if(isset($library['version_arguments'][0]))
{
if($class)
{
$params = array_merge(array($library), $library['version_arguments']);
$library['version'] = e107::callMethod($class, $library['version_callback'], $params);
}
}
else
{
if($class)
{
$library['version'] = e107::callMethod($class, $library['version_callback'], $library, $library['version_arguments']);
}
}
}
if(empty($library['version']))
{
$library['error'] = LAN_LIBRARY_MANAGER_10;
$replace_with = array($library['name']);
$library['error_message'] = e107::getParser()->lanVars(LAN_LIBRARY_MANAGER_04, $replace_with, true);
return $library;
}
}
// Determine to which supported version the installed version maps.
if(!empty($library['versions']))
{
ksort($library['versions']);
$version = 0;
foreach($library['versions'] as $supported_version => $version_properties)
{
if(version_compare($library['version'], $supported_version, '>='))
{
$version = $supported_version;
}
}
if(!$version)
{
$library['error'] = LAN_LIBRARY_MANAGER_11;
$replace_with = array($library['version'], $library['name']);
$library['error_message'] = e107::getParser()->lanVars(LAN_LIBRARY_MANAGER_05, $replace_with, true);
return $library;
}
// Apply version specific definitions and overrides.
$library = array_merge($library, $library['versions'][$version]);
unset($library['versions']);
}
// Check each variant if it is installed.
if(!empty($library['variants']))
{
foreach($library['variants'] as $variant_name => &$variant)
{
// If no variant callback has been set, assume the variant to be installed.
if(!isset($variant['variant_callback']))
{
$variant['installed'] = true;
}
else
{
$variant['installed'] = false;
$class = false;
if(varset($library['plugin'], false))
{
$class = e107::getAddon($library['plugin'], 'e_library');
}
elseif(varset($library['theme'], false))
{
// e107::getAddon() does not support theme folders.
if(is_readable(e_THEME . $library['theme'] . '/theme_library.php'))
{
e107_require_once(e_THEME . $library['theme'] . '/theme_library.php');
$addonClass = 'theme_library';
if(class_exists($addonClass))
{
$class = new $addonClass();
}
}
// e107::getAddon() does not support theme folders.
if(is_readable(e_THEME . $library['theme'] . '/admin_theme_library.php'))
{
e107_require_once(e_THEME . $library['theme'] . '/admin_theme_library.php');
$addonClass = 'admin_theme_library';
if(class_exists($addonClass))
{
$class = new $addonClass();
}
}
}
// We support both a single parameter, which is an associative array, and an indexed array of
// multiple parameters.
if(isset($variant['variant_arguments'][0]))
{
if($class)
{
$params = array_merge(array($library, $variant_name), $variant['variant_arguments']);
$variant['installed'] = e107::callMethod($class, $library['variant_callback'], $params);
}
}
else
{
if($class)
{
// Can't use e107::callMethod(), because it only supports 2 params.
if(method_exists($class, $variant['variant_callback']))
{
// Call PLUGIN/THEME_library::VARIANT_CALLBACK().
$method = $variant['variant_callback'];
$variant['installed'] = $class->$method($library, $variant_name, $variant['variant_arguments']);
}
}
}
if(!$variant['installed'])
{
$variant['error'] = LAN_NOT_FOUND;
$replace_with = array($variant_name, $library['name']);
$variant['error_message'] = e107::getParser()->lanVars(LAN_LIBRARY_MANAGER_06, $replace_with, true);
}
}
}
}
// If we end up here, the library should be usable.
$library['installed'] = true;
// Invoke callbacks in the 'post_detect' group.
$this->invoke('post_detect', $library);
return $library;
}
/**
* Loads a library.
*
* @param $name
* The name of the library to load.
* @param $variant
* The name of the variant to load. Note that only one variant of a library can be loaded within a single
* request. The variant that has been passed first is used; different variant names in subsequent calls are
* ignored.
*
* @return mixed
* An associative array of the library information as returned from config(). The top-level properties
* contain the effective definition of the library (variant) that has been loaded. Additionally:
* - installed: Whether the library is installed, as determined by detect().
* - loaded: Either the amount of library files that have been loaded, or FALSE if the library could not be
* loaded. See MYPLUGIN_library::config() for more information.
*/
public function load($name, $variant = null, $types = ['js', 'css'])
{
// Re-use the statically cached value to save memory.
static $loaded;
if(!isset($loaded[$name]))
{
$cache = e107::getCache();
$cache_context = (defset('e_ADMIN_AREA', false) == true) ? 'AdminArea' : 'UserArea';
$cacheID = 'Library_' . $cache_context . '_' . e107::getParser()->filter($name, 'file');
$cached = $cache->retrieve($cacheID, false, true, true);
if($cached)
{
$library = e107::unserialize($cached);
}
if(empty($library))
{
$library = $this->detect($name);
$cacheData = e107::serialize($library, 'json');
$cache->set($cacheID, $cacheData, true, true, true);
}
// Exit early if the library was not found.
if($library === false)
{
$loaded[$name] = $library;
return $loaded[$name];
}
// If a variant was specified, override the top-level properties with the variant properties.
if(isset($variant))
{
// Ensure that the $variant key exists, and if it does not, set its 'installed' property to FALSE by
// default. This will prevent the loading of the library files below.
$library['variants'] += array($variant => array('installed' => false));
$library = array_merge($library, $library['variants'][$variant]);
}
// Regardless of whether a specific variant was requested or not, there can only be one variant of a
// library within a single request.
unset($library['variants']);
// TODO:
// Invoke callbacks in the 'pre_dependencies_load' group.
$this->invoke('pre_dependencies_load', $library);
// If the library (variant) is installed, load it.
$library['loaded'] = false;
if($library['installed'])
{
// Load library dependencies.
if(isset($library['dependencies']))
{
foreach($library['dependencies'] as $dependency)
{
$this->load($dependency);
}
}
// TODO:
// Invoke callbacks in the 'pre_load' group.
$this->invoke('pre_load', $library);
// Load all the files associated with the library.
$library['loaded'] = $this->loadFiles($library, $types);
// TODO:
// Invoke callbacks in the 'post_load' group.
$this->invoke('post_load', $library);
}
$loaded[$name] = $library;
}
return $loaded[$name];
}
/**
* Gets the path of a library.
*
* @param $name
* The machine name of a library to return the path for.
*
* @return string
* The path to the specified library or FALSE if the library wasn't found.
*/
private function detectPath($name)
{
static $libraries;
if(!isset($libraries))
{
$libraries = $this->getLibraries();
}
$path = '';
if(!isset($libraries[$name]))
{
return false;
}
else
{
$path .= $libraries[$name];
}
return $path;
}
/**
* Returns an array of library directories.
*
* @return array
* A list of library directories.
*/
private function getLibraries()
{
$dir = e_WEB . 'lib';
$directories = array();
// Retrieve list of directories.
$file = e107::getFile();
$dirs = $file->get_dirs($dir);
foreach($dirs as $dirName)
{
$directories[$dirName] = "{e_WEB}lib/$dirName";
}
return $directories;
}
/**
* Returns with the selected property of a library.
*
* @param string $library
* Library machine name. For example: bootstrap
*
* @param string $property
* The property name. For example: library_path
*
* @return mixed
*/
public function getProperty($library, $property)
{
$lib = self::info($library);
return varset($lib[$property], false);
}
/**
* Return full path to a library in different formats.
* @param string $library
* The library name eg. bootstrap
*
* @param null $mode
* The mode: null | 'full' | 'abs'
*
* @return string
*/
public function getPath($library, $mode=null)
{
$path = self::getProperty($library, 'library_path').'/'. self::getProperty($library, 'path');
return trim(e107::getParser()->replaceConstants($path,$mode),'/') .'/';
}
/**
* Returns information about registered libraries.
*
* The returned information is unprocessed; i.e., as registered by plugins.
*
* @param string $library
* (optional) The machine name of a library to return registered information for. If omitted, information
* about all registered libraries is returned.
*
* @param bool $force - ignore any cached values and reload.
*
* @return array|false
* An associative array containing registered information for all libraries, the registered information for the
* library specified by $name, or FALSE if the library $name is not registered.
*/
public function &info($library = null, $force = false)
{
// This static cache is re-used by detect() to save memory.
if($force === false)
{
static $libraries;
}
if(!isset($libraries))
{
$libraries = array();
$coreLibrary = new core_library();
$info = $coreLibrary->config();
if(is_array($info))
{
foreach($info as $machine_name => $properties)
{
$properties['info_type'] = 'core';
$libraries[$machine_name] = $properties;
}
}
$plugins = array();
$themes = array();
// Gather information from PLUGIN_library::config().
$pluginInfo = e107::getAddonConfig('e_library', 'library'); // 'config' is the default.
foreach($pluginInfo as $plugin => $info)
{
foreach($info as $machine_name => $properties)
{
$properties['info_type'] = 'plugin';
$properties['plugin'] = $plugin;
$libraries[$machine_name] = $properties;
$plugins[] = $plugin; // This plugin has a valid e_library implementation.
}
}
$themes[] = array(
'name' => e107::getPref('sitetheme'),
'file' => 'theme_library',
'class' => 'theme_library',
);
$themes[] = array(
'name' => e107::getPref('admintheme'),
'file' => 'admin_theme_library',
'class' => 'admin_theme_library',
);
foreach($themes as $theme)
{
if(is_readable(e_THEME . $theme['name'] . '/' . $theme['file'] . '.php'))
{
e107_require_once(e_THEME . $theme['name'] . '/' . $theme['file'] . '.php');
$info = e107::callMethod($theme['class'], 'config');
if(is_array($info))
{
foreach($info as $machine_name => $properties)
{
$properties['info_type'] = 'theme';
$properties['theme'] = $theme['name'];
$libraries[$machine_name] = $properties;
}
}
}
}
// Provide defaults.
foreach($libraries as $machine_name => &$properties)
{
$this->infoDefaults($properties, $machine_name);
}
// Alter config array. For example, change CDN provider.
$coreLibrary->config_alter($libraries);
// Allow enabled plugins (with e_library.php file) to alter the registered libraries.
foreach($plugins as $plugin)
{
$class = e107::getAddon($plugin, 'e_library');
if($class && method_exists($class, 'config_alter'))
{
// The library definitions are passed by reference.
$class->config_alter($libraries);
}
}
// Allow enabled themes to alter the registered libraries.
foreach($themes as $theme)
{
if(is_readable(e_THEME . $theme['name'] . '/' . $theme['file'] . '.php'))
{
e107_require_once(e_THEME . $theme['name'] . '/' . $theme['file'] . '.php');
if(class_exists($theme['class']))
{
$class = new $theme['class']();
if(method_exists($class, 'config_alter'))
{
// We cannot use e107::callMethod() because need to pass variable by reference.
$class->config_alter($libraries);
}
}
}
}
// TODO:
// Invoke callbacks in the 'info' group.
foreach($libraries as &$properties)
{
$this->invoke('info', $properties);
}
}
if(isset($library))
{
if(!empty($libraries[$library]))
{
return $libraries[$library];
}
else
{
$false = false;
return $false;
}
}
return $libraries;
}
/**
* Applies default properties to a library definition.
*
* @param array $library
* An array of library information, passed by reference.
* @param string $name
* The machine name of the passed-in library.
*
* @return array
*/
private function infoDefaults(&$library, $name)
{
$library += array(
'machine_name' => $name,
'name' => $name,
'vendor_url' => '',
'download_url' => '',
'path' => '',
'library_path' => null,
'version_callback' => 'getVersion',
'version_arguments' => array(),
'files' => array(),
'dependencies' => array(),
'variants' => array(),
'versions' => array(),
'integration_files' => array(),
'callbacks' => array(),
);
$library['callbacks'] += array(
'info' => array(),
'pre_detect' => array('preDetect'),
'post_detect' => array(),
'pre_dependencies_load' => array(),
'pre_load' => array('preLoad'),
'post_load' => array(),
);
// Add our own callbacks before any others.
array_unshift($library['callbacks']['info'], 'prepareFiles');
array_unshift($library['callbacks']['post_detect'], 'detectDependencies');
return $library;
}
/**
* Library info callback to make all 'files' properties consistent.
*
* This turns libraries' file information declared as e.g.
* @code
* $library['files']['js'] = array('example_1.js', 'example_2.js');
* @endcode
* into
* @code
* $library['files']['js'] = array(
* 'example_1.js' => array(),
* 'example_2.js' => array(),
* );
* @endcode
* It does the same for the 'integration_files' property.
*
* @param $library
* An associative array of library information or a part of it, passed by reference.
* @param $version
* If the library information belongs to a specific version, the version string. NULL otherwise.
* @param $variant
* If the library information belongs to a specific variant, the variant name. NULL otherwise.
*/
private function prepareFiles(&$library, $version = null, $variant = null)
{
// Both the 'files' property and the 'integration_files' property contain file declarations, and we want to make
// both consistent.
$file_types = array();
if(isset($library['files']))
{
$file_types[] = &$library['files'];
}
if(isset($library['integration_files']))
{
// Integration files are additionally keyed by plugin.
foreach($library['integration_files'] as &$integration_files)
{
$file_types[] = &$integration_files;
}
}
foreach($file_types as &$files)
{
// Go through all supported types of files.
foreach(array('js', 'css', 'php') as $type)
{
if(isset($files[$type]))
{
foreach($files[$type] as $key => $value)
{
// Unset numeric keys and turn the respective values into keys.
if(is_numeric($key))
{
$files[$type][$value] = array();
unset($files[$type][$key]);
}
}
}
}
}
}
/**
* Library post detect callback to process and detect dependencies.
*
* It checks whether each of the dependencies of a library are installed and available in a compatible version.
*
* @param $library
* An associative array of library information or a part of it, passed by reference.
* @param $version
* If the library information belongs to a specific version, the version string. NULL otherwise.
* @param $variant
* If the library information belongs to a specific variant, the variant name. NULL otherwise.
*/
private function detectDependencies(&$library, $version = null, $variant = null)
{
if(isset($library['dependencies']))
{
foreach($library['dependencies'] as &$dependency_string)
{
$dependency_info = $this->parseDependency($dependency_string);
$dependency = $this->detect($dependency_info['name']);
if(!$dependency['installed'])
{
$library['installed'] = false;
$library['error'] = LAN_LIBRARY_MANAGER_07;
$replace_with = array($dependency['name'], $library['name']);
$library['error_message'] = e107::getParser()->lanVars(LAN_LIBRARY_MANAGER_01, $replace_with, true);
}
elseif($this->checkIncompatibility($dependency_info, $dependency['version']))
{
$library['installed'] = false;
$library['error'] = LAN_LIBRARY_MANAGER_08;
$replace_with = array($dependency['version'], $library['name'], $library['name']);
$library['error_message'] = e107::getParser()->lanVars(LAN_LIBRARY_MANAGER_02, $replace_with, true);
}
// Remove the version string from the dependency, so load() can load the libraries directly.
$dependency_string = $dependency_info['name'];
}
}
}
/**
* Invokes library callbacks.
*
* @param $group
* A string containing the group of callbacks that is to be applied. Should be either 'info', 'post_detect'.
* @param $library
* An array of library information, passed by reference.
*/
private function invoke($group, &$library)
{
// When introducing new callback groups in newer versions, stale cached library information somehow reaches
// this point during the database update before clearing the library cache.
if(empty($library['callbacks'][$group]))
{
return;
}
foreach($library['callbacks'][$group] as $callback)
{
$this->traverseLibrary($library, $callback);
}
}
/**
* Helper function to apply a callback to all parts of a library.
*
* Because library declarations can include variants and versions, and those version declarations can in turn
* include variants, modifying e.g. the 'files' property everywhere it is declared can be quite cumbersome, in
* which case this helper function is useful.
*
* @param $library
* An array of library information, passed by reference.
* @param $callback
* A string containing the callback to apply to all parts of a library.
*/
private function traverseLibrary(&$library, $callback)
{
// If callback belongs to $this class.
if(method_exists($this, $callback))
{
// Always apply the callback to the top-level library.
// Params: $library, $version, $variant
$this->{$callback}($library, null, null);
// Apply the callback to versions.
if(isset($library['versions']))
{
foreach($library['versions'] as $version_string => &$version)
{
$this->{$callback}($version, $version_string, null);
// Versions can include variants as well.
if(isset($version['variants']))
{
foreach($version['variants'] as $version_variant_name => &$version_variant)
{
$this->{$callback}($version_variant, $version_string, $version_variant_name);
}
}
}
}
// Apply the callback to variants.
if(isset($library['variants']))
{
foreach($library['variants'] as $variant_name => &$variant)
{
$this->$callback($variant, null, $variant_name);
}
}
}
//else
// {
// TODO: Provide the ability to use third-party callbacks (are defined in e_library.php files) for groups:
// 'info', 'pre_detect', 'post_detect', 'pre_dependencies_load', 'pre_load', 'post_load'
// }
}
/**
* Loads a library's files.
*
* @param $library
* An array of library information as returned by info().
*
* @return int
* The number of loaded files.
*/
private function loadFiles($library, $types = array('js', 'css'))
{
$siteTheme = e107::getPref('sitetheme');
$adminTheme = e107::getPref('admintheme');
// Load integration_files.
if(!empty($library['post_load_integration_files']) && !$library['post_load_integration_files'] && !empty($library['integration_files']))
{
foreach($library['integration_files'] as $provider => $files)
{
// If provider is an installed plugin.
if(e107::isInstalled($provider))
{
$this->loadFiles(array(
'files' => $files,
'path' => '',
'library_path' => e_PLUGIN . $provider,
'post_load_integration_files' => false,
));
}
// If provider is the admin theme, we only allow it for admin pages.
elseif(e_ADMIN_AREA && $provider == $adminTheme)
{
$this->loadFiles(array(
'files' => $files,
'path' => '',
'library_path' => e_THEME . $provider,
'post_load_integration_files' => false,
));
}
// If provider is the site theme, we only allow it on user areas.
elseif(!deftrue(e_ADMIN_AREA, false) && $provider == $siteTheme)
{
$this->loadFiles(array(
'files' => $files,
'path' => '',
'library_path' => e_THEME . $provider,
'post_load_integration_files' => false,
));
}
}
}
// Construct the full path to the library for later use.
$path = e107::getParser()->replaceConstants($library['library_path']);
$path = ($library['path'] !== '' ? rtrim($path, '/') . '/' . $library['path'] : $path);
$path = rtrim($path, '/');
// Count the number of loaded files for the return value.
$count = 0;
// Load both the JavaScript and the CSS files.
foreach($types as $type)
{
if(!empty($library['files'][$type]))
{
foreach($library['files'][$type] as $data => $options)
{
// If the value is not an array, it's a filename and passed as first (and only) argument.
if(!is_array($options))
{
$data = $options;
$options = array();
}
// In some cases, the first parameter ($data) is an array. Arrays can't be passed as keys in PHP,
// so we have to get $data from the value array.
if(is_numeric($data))
{
$data = $options['data'];
unset($options['data']);
}
// Prepend the library_path to the file name.
$data = "$path/$data";
// Apply the default zone if the zone isn't explicitly given.
if(!isset($options['zone']))
{
$options['zone'] = ($type == 'js') ? 2 : 2; // TODO: default zones.
}
// Apply the default type if the type isn't explicitly given.
if(!isset($options['type']))
{
$options['type'] = 'url';
}
if($type == 'js')
{
e107::js($options['type'], $data, $options, $options['zone']);
}
elseif($type == 'css')
{
e107::getJs()->libraryCSS($data); // load before others.
// e107::css($options['type'], $data, null);
}
$count++;
}
}
}
// Load PHP files.
if(!empty($library['files']['php']))
{
foreach($library['files']['php'] as $file => $array)
{
$file_path1 = $path . '/' . $file;
$file_path2 = e_ROOT . $path . '/' . $file;
if(file_exists($file_path1))
{
$this->_requireOnce($file_path1);
$count++;
}
elseif(file_exists($file_path2))
{
$this->_requireOnce($file_path2);
$count++;
}
}
}
// Load integration_files.
if(!empty($library['post_load_integration_files']) && $library['post_load_integration_files'] && !empty($library['integration_files']))
{
foreach($library['integration_files'] as $provider => $files)
{
// If provider is an installed plugin.
if(e107::isInstalled($provider))
{
$this->loadFiles(array(
'files' => $files,
'path' => '',
'library_path' => e_PLUGIN . $provider,
'post_load_integration_files' => false,
));
}
// If provider is the admin theme, we only allow it for admin pages.
elseif(e_ADMIN_AREA && $provider == $adminTheme)
{
$this->loadFiles(array(
'files' => $files,
'path' => '',
'library_path' => e_THEME . $provider,
'post_load_integration_files' => false,
));
}
// If provider is the site theme, we only allow it on user areas.
elseif(!deftrue(e_ADMIN_AREA, false) && $provider == $siteTheme)
{
$this->loadFiles(array(
'files' => $files,
'path' => '',
'library_path' => e_THEME . $provider,
'post_load_integration_files' => false,
));
}
}
}
return $count;
}
/**
* Wrapper function for require_once.
*
* A library file could set a $path variable in file scope. Requiring such a file directly in loadFiles()
* would lead to the local $path variable being overridden after the require_once statement. This would break
* loading further files. Therefore we use this trivial wrapper which has no local state that can be tampered with.
*
* @param $file_path
* The file path of the file to require.
*/
private function _requireOnce($file_path)
{
// TODO: use e107_require_once() instead?
require_once $file_path;
}
/**
* Gets the version information from an arbitrary library.
*
* @param $library
* An associative array containing all information about the library.
* @param $options
* An associative array containing with the following keys:
* - file: The filename to parse for the version, relative to the library path. For example: 'docs/changelog.txt'.
* - pattern: A string containing a regular expression (PCRE) to match the library version. For example:
* '@version\s+([0-9a-zA-Z\.-]+)@'. Note that the returned version is not the match of the entire pattern (i.e.
* '@version 1.2.3' in the above example) but the match of the first sub-pattern (i.e. '1.2.3' in the above example).
* - lines: (optional) The maximum number of lines to search the pattern in. Defaults to 20.
* - cols: (optional) The maximum number of characters per line to take into account. Defaults to 200. In case of
* minified or compressed files, this prevents reading the entire file into memory.
*
* @return mixed
* A string containing the version of the library. Or null.
*/
private function getVersion($library, $options)
{
// Provide defaults.
$options += array(
'file' => '',
'pattern' => '',
'lines' => 20,
'cols' => 200,
);
$libraryPath = e107::getParser()->replaceConstants($library['library_path']);
$libraryPath = ($library['path'] !== '' ? rtrim($libraryPath, '/') . '/' . $library['path'] : $libraryPath);
$libraryPath = rtrim($libraryPath, '/');
$file = $libraryPath . '/' . $options['file'];
if(empty($options['file']))
{
return;
}
// If remote file (e.g. CDN URL)... we download file to temp, and get version number.
// The library will be cached with version number, so this only run once per library.
if(strpos($file, 'http') === 0)
{
$content = e107::getFile()->getRemoteContent($file);
$tmpFile = tempnam(sys_get_temp_dir(), 'lib_');
if($tmpFile)
{
file_put_contents($tmpFile, $content);
$file = $tmpFile;
}
}
if(!file_exists($file))
{
return;
}
$file = fopen($file, 'r');
while($options['lines'] && $line = fgets($file, $options['cols']))
{
if(preg_match($options['pattern'], $line, $version))
{
fclose($file);
// If downloaded file, we need to unlink it from temp.
if(isset($tmpFile) && file_exists($tmpFile))
{
unlink($tmpFile);
}
return $version[1];
}
$options['lines']--;
}
fclose($file);
// If downloaded file, we need to unlink it from temp.
if(isset($tmpFile) && file_exists($tmpFile))
{
unlink($tmpFile);
}
return;
}
/**
* Parses a dependency for comparison by checkIncompatibility().
*
* @param $dependency
* A dependency string, which specifies a plugin dependency, and versions that are supported. Supported formats
* include:
* - 'plugin'
* - 'plugin (>=version, version)'
*
* @return array
* An associative array with three keys:
* - 'name' includes the name of the thing to depend on (e.g. 'foo').
* - 'original_version' contains the original version string (which can be used in the UI for reporting
* incompatibilities).
* - 'versions' is a list of associative arrays, each containing the keys 'op' and 'version'. 'op' can be one of:
* '=', '==', '!=', '<>', '<', '<=', '>', or '>='. 'version' is one piece like '4.5-beta3'.
* Callers should pass this structure to checkIncompatibility().
*/
private function parseDependency($dependency)
{
$value = array();
// We use named subpatterns and support every op that version_compare supports. Also, op is optional and
// defaults to equals.
$p_op = '(?P<operation>!=|==|=|<|<=|>|>=|<>)?';
$p_major = '(?P<major>\d+)';
// By setting the minor version to x, branches can be matched.
$p_minor = '(?P<minor>(?:\d+|x)(?:-[A-Za-z]+\d+)?)';
$parts = explode('(', $dependency, 2);
$value['name'] = trim($parts[0]);
if(isset($parts[1]))
{
$value['original_version'] = ' (' . $parts[1];
foreach(explode(',', $parts[1]) as $version)
{
if(preg_match("/^\s*$p_op\s*$p_major\.$p_minor/", $version, $matches))
{
$op = !empty($matches['operation']) ? $matches['operation'] : '=';
if($matches['minor'] == 'x')
{
// "2.x" to mean any version that begins with "2" (e.g. 2.0, 2.9 are all "2.x").
// PHP's version_compare(), on the other hand, treats "x" as a string; so to version_compare(),
// "2.x" is considered less than 2.0. This means that >=2.x and <2.x are handled by
// version_compare() as we need, but > and <= are not.
if($op == '>' || $op == '<=')
{
$matches['major']++;
}
// Equivalence can be checked by adding two restrictions.
if($op == '=' || $op == '==')
{
$value['versions'][] = array('op' => '<', 'version' => ($matches['major'] + 1) . '.x');
$op = '>=';
}
}
$value['versions'][] = array('op' => $op, 'version' => $matches['major'] . '.' . $matches['minor']);
}
}
}
return $value;
}
/**
* Checks whether a version is compatible with a given dependency.
*
* @param $v
* The parsed dependency structure from parseDependency().
* @param $current_version
* The version to check against (like 4.2).
*
* @return mixed
* NULL if compatible, otherwise the original dependency version string that caused the incompatibility.
*
* @see parseDependency()
*/
private function checkIncompatibility($v, $current_version)
{
if(!empty($v['versions']))
{
foreach($v['versions'] as $required_version)
{
if((isset($required_version['op'])))
{
if(!version_compare($current_version, $required_version['version'], $required_version['op']))
{
return $v['original_version'];
}
}
}
}
}
/**
* Alters library information before detecting.
*/
private function preDetect(&$library)
{
if(empty($library['machine_name']))
{
return;
}
// Prevent plugins/themes from altering libraries on Admin UI.
if(defset('e_ADMIN_AREA', false) == true)
{
$coreLibrary = new core_library();
$coreLibs = $coreLibrary->config();
if (isset($coreLibs[$library['machine_name']])) {
$coreLib = $coreLibs[$library['machine_name']];
$library = array_replace_recursive($coreLib, array_replace_recursive($library, $coreLib));
}
}
}
/**
* Alters library information before loading.
*/
private function preLoad(&$library)
{
if(empty($library['machine_name']))
{
return;
}
$excluded = $this->getExcludedLibraries();
if(empty($excluded))
{
return;
}
// Make sure we have the name without cdn prefix.
$basename = str_replace('cdn.', '', $library['machine_name']);
// If this library (or the CDN version of this library) is excluded
// by the theme is currently used.
if (in_array($basename, $excluded) || in_array('cdn.' . $basename, $excluded))
{
unset($library['files']['css']);
if (!empty($library['variants']))
{
foreach($library['variants'] as &$variant)
{
if(!empty($variant['files']['css']))
{
unset($variant['files']['css']);
}
}
}
}
}
/**
* Get excluded libraries.
*
* @return array
*/
public function getExcludedLibraries()
{
// This static cache is re-used by preLoad() to save memory.
static $excludedLibraries;
if(!isset($excludedLibraries))
{
$excludedLibraries = array();
$exclude = e107::getTheme('current', false)->cssAttribute('auto', 'exclude');
if($exclude)
{
// Split string into array and remove whitespaces.
$excludedLibraries = array_map('trim', explode(',', $exclude));
}
}
return $excludedLibraries;
}
}