52
0
plg_extension_componentbuil.../componentbuilderlanguagepackaging.php

794 lines
22 KiB
PHP

<?php
/**
* @package Joomla.Component.Builder
*
* @created 30th April, 2015
* @author Llewellyn van der Merwe <https://dev.vdm.io>
* @git Joomla Component Builder <https://git.vdm.dev/joomla/Component-Builder-Pro>
* @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
use Joomla\CMS\Application\CMSApplication;
use Joomla\CMS\Plugin\CMSPlugin;
JLoader::register('ComponentbuilderHelper', JPATH_ADMINISTRATOR . '/components/com_componentbuilder/helpers/componentbuilder.php');
/**
* Extension - Componentbuilder Language Packaging plugin.
*
* @package ComponentbuilderLanguagePackaging
* @since 1.0.1
*/
class PlgExtensionComponentbuilderLanguagePackaging extends CMSPlugin
{
/**
* Affects constructor behavior. If true, language files will be loaded automatically.
*
* @var boolean
* @since 1.0.0
*/
protected $autoloadLanguage = true;
/**
* Database object
*
* @var DatabaseDriver
* @since 1.0.0
*/
protected $db;
/**
* Application object
*
* @var CMSApplication
* @since 1.0.0
*/
protected $app;
/**
* The percentage before a language can be added
*
* @var int
* @since 1.0.0
*/
protected $percentageLanguageAdd;
/**
* The percentage before a language can be added
*
* @var int
* @since 1.0.0
*/
protected $percentageLanguageAddOveride = 200;
/**
* The languages names
*
* @var array
* @since 1.0.0
*/
protected $languageNames = array();
/**
* The language building tracker
*
* @var array
* @since 1.0.0
*/
protected $languageTracker = array();
/**
* The languages not added that should be added
*
* @var array
* @since 1.0.0
*/
protected $langNot;
/**
* The languages added
*
* @var array
* @since 1.0.0
*/
protected $langSet;
/**
* The should the site folder be removed
*
* @var bool
* @since 1.0.0
*/
protected $removeSiteFolder;
/**
* The should the site folder be removed
*
* @var bool
* @since 1.0.0
*/
protected $removeSiteEditFolder;
/**
* The component path
*
* @var string
* @since 1.0.0
*/
protected $componentPath;
/**
* The compiler path
*
* @var string
* @since 1.0.0
*/
protected $compilerPath;
/**
* The temporal path
*
* @var string
* @since 1.0.0
*/
protected $tempPath;
/**
* The joomla version
*
* @var string
* @since 1.0.0
*/
protected $joomlaVersion;
/**
* The component version
*
* @var string
* @since 1.0.0
*/
protected $component_version;
/**
* The component name
*
* @var string
* @since 1.0.0
*/
protected $componentCodeName;
/**
* The number of files
*
* @var int
* @since 1.0.0
*/
protected $fileCount;
/**
* The number of lines
*
* @var bool
* @since 1.0.0
*/
protected $lineCount;
/**
* The hash placeholder
*
* @var string
* @since 1.0.0
*/
protected $hhh = '#' . '#' . '#';
/**
* The file content static values
*
* @var array
* @since 1.0.0
*/
protected $fileContentStatic;
/*
* The line numbers Switch
*
* @var boolean
* @since 1.0.0
*/
protected $debugLinenr = false;
/**
* The Active Components
*
* @var array
* @since 1.0.0
*/
protected $activeComponents = array();
/**
* The Active Components Names
*
* @var array
* @since 1.0.0
*/
protected $activeComponentsNames = array();
/**
* The Languages
*
* @var array
* @since 1.0.0
*/
protected $languages = array();
/**
* The Language build details
*
* @var array
* @since 1.0.0
*/
protected $buildDetails = array();
/**
* The Excluded Languages
*
* @var array
* @since 1.0.0
*/
protected $excludedLang = array();
/**
* The Active Language
*
* @var string
* @since 1.0.0
*/
protected $langTag;
/**
* Event Triggered in the compiler [on Before Model Component Data]
*
* @return void
*
* @since 1.0
*/
public function jcb_ce_onBeforeModelComponentData(&$context, &$component)
{
// add the privacy
$component->params = (isset($component->params) && ComponentbuilderHelper::checkJson($component->params)) ? json_decode($component->params, true) : $component->params;
if (ComponentbuilderHelper::checkArray($component->params) && isset($component->params['language_options']) &&
isset($component->params['language_options']['activate']) && $component->params['language_options']['activate'] == 1)
{
// load the admin component details
$this->activeComponents[$component->id] = $context;
$this->activeComponentsNames[$component->id] = ComponentbuilderHelper::safeString($component->name_code);
$this->activeComponentsRealNames[$component->id] = $component->name;
// add excluded list of languages
if (isset($component->params['language_options']['languages']))
{
$this->excludedLang[$component->id] = $component->params['language_options']['languages'];
}
else
{
$this->excludedLang[$component->id] = array();
}
// now set the component add languages if we should use local (2)
if (isset($component->params['language_options']['use_percentagelanguageadd']) && $component->params['language_options']['use_percentagelanguageadd'] == 2)
{
$this->percentageLanguageAddOveride = $component->params['language_options']['percentagelanguageadd'];
}
}
}
/**
* Event Triggered in the compiler [on After Get]
*
* @return void
*
* @since 1.0
*/
public function jcb_ce_onAfterGet(&$context, $compiler)
{
// get component id
$id = (int) filter_var($context, FILTER_SANITIZE_NUMBER_INT);
// check if there is active
if (ComponentbuilderHelper::checkArray($this->activeComponents)
&& isset($this->activeComponents[$id])
&& $this->percentageLanguageAddOveride != 200)
{
$compiler->percentageLanguageAdd = $this->percentageLanguageAddOveride;
}
}
/**
* Event Triggered in the compiler [on Before Set Lang File Data]
*
* @return void
*
* @since 1.0
*/
public function jcb_ce_onBeforeSetLangFileData(&$context, $compiler)
{
// lets map some arrays to the plugin for later use
$this->compilerPath = $compiler->compilerPath;
$this->tempPath = $compiler->tempPath;
$this->langTag = $compiler->langTag;
$this->debugLinenr = $compiler->debugLinenr;
$this->component_version = $compiler->componentData->component_version;
$this->joomlaVersion = $compiler->joomlaVersion;
$this->percentageLanguageAdd = $compiler->percentageLanguageAdd;
$this->langNot = &$compiler->langNot;
$this->langSet = &$compiler->langSet;
$this->removeSiteFolder = $compiler->removeSiteFolder;
$this->removeSiteEditFolder = $compiler->removeSiteEditFolder;
$this->componentPath = $compiler->componentPath;
$this->componentCodeName = $compiler->componentCodeName;
$this->fileCount = &$compiler->fileCount;
$this->lineCount = &$compiler->lineCount;
$this->fileContentStatic = &$compiler->fileContentStatic;
$this->hhh = $compiler->hhh;
}
/**
* Event Triggered in the compiler [on Before Build Plugin Lang Files]
*
* @return void
*
* @since 1.0
*/
public function jcb_ce_onBeforeBuildPluginLangFiles(&$context, &$plugin, &$languages, &$langTag)
{
// get component id
$id = (int) filter_var($context, FILTER_SANITIZE_NUMBER_INT);
// check if there is active
if (ComponentbuilderHelper::checkArray($this->activeComponents) && isset($this->activeComponents[$id]))
{
// set file name
$file_name = 'plg_' . strtolower($plugin->group) . '_' . strtolower($plugin->code_name);
// extrude the languages that should not remain in the plugin
$this->extrudeLanguages($id, $languages, $langTag, $file_name, 'admin');
}
}
/**
* Event Triggered in the compiler [on Before Build Module Lang Files]
*
* @return void
*
* @since 1.0
*/
public function jcb_ce_onBeforeBuildModuleLangFiles(&$context, &$module, &$languages, &$langTag)
{
// get component id
$id = (int) filter_var($context, FILTER_SANITIZE_NUMBER_INT);
// check if there is active
if (ComponentbuilderHelper::checkArray($this->activeComponents) && isset($this->activeComponents[$id]))
{
// extrude the languages that should not remain in the module
$this->extrudeLanguages($id, $languages, $langTag, $module->file_name, $module->target_client);
}
}
/**
* Event Triggered in the compiler [on Before Build All Lang Files]
*
* @return void
*
* @since 1.0
*/
public function jcb_ce_onBeforeBuildAllLangFiles(&$context, &$languages, &$langTag)
{
// get component id
$id = (int) filter_var($context, FILTER_SANITIZE_NUMBER_INT);
// check if there is active
if (ComponentbuilderHelper::checkArray($this->activeComponents) && isset($this->activeComponents[$id]))
{
// set file name
$file_name = 'com_' . $this->activeComponentsNames[$id];
// extrude the languages that should not remain in the module
$this->extrudeLanguages($id, $languages, $langTag, $file_name);
}
// build the language packages
$this->buildLanguages($id, $langTag);
}
/**
* Extruder of the languages
*
* @return void
*
* @since 1.0
*/
protected function extrudeLanguages(&$id, &$languages, &$langTag, &$file_name, $target_client = 'both')
{
$mainLangLoader = array();
// check if this id was set before
if (!isset($this->languages[$id]))
{
$this->languages[$id] = array();
$this->buildDetails[$id] = array();
}
// check if this file name was set before
if (!isset($this->languages[$id][$file_name]))
{
$this->languages[$id][$file_name] = array();
}
// set all the extra languages not excluded
foreach ($languages as $key => $language)
{
if ($key !== $langTag && ComponentbuilderHelper::checkArray($language) && (!isset($this->excludedLang[$id]) || !in_array($key, $this->excludedLang[$id])))
{
// add to our bucket
$this->languages[$id][$file_name][$key] = $language;
// remove from the JCB build
unset($languages[$key]);
}
// count the area strings
if ($langTag === $key)
{
foreach ($language as $area => $languageStrings)
{
$mainLangLoader[$area] = count($languageStrings);
}
}
}
// store details for build
$this->buildDetails[$id][$file_name] = array($langTag => $mainLangLoader, 'target_client' => $target_client);
}
/**
* Start the building of the languages packages
*
* @return void
*
*/
protected function buildLanguages(&$id, &$langTag)
{
if (isset($this->languages[$id]) && ComponentbuilderHelper::checkArray($this->languages[$id]))
{
// rest xml array
$langXML = array();
$langNames = array();
$langPackages = array();
$langZIPNames = array();
$langXMLNames = array();
$versionName = $this->activeComponentsNames[$id] . '_v' . str_replace('.', '_', $this->component_version . '__J' . $this->joomlaVersion);
foreach ($this->languages[$id] as $file_name => $languages)
{
if (ComponentbuilderHelper::checkArray($languages) && isset($this->buildDetails[$id][$file_name][$langTag]))
{
// get the main lang loader
$mainLangLoader = $this->buildDetails[$id][$file_name][$langTag];
// get the target client
$target_client = $this->buildDetails[$id][$file_name]['target_client'];
foreach ($languages as $tag => $areas)
{
// trim the tag
$tag = trim($tag);
// get language name
$langName = $this->getLanguageName($tag);
$langCodeName = ComponentbuilderHelper::safeString($langName, 'F');
// set the file folder name
$langFolderFileName = $langCodeName . '_' . $versionName;
// set the main folder path
$main_path = $this->compilerPath . '/' . $langFolderFileName . '/';
// set the language name for later
$langNames[$main_path] = $langName;
// set the lang zip name for later
$langZIPNames[$main_path] = $langFolderFileName;
// set the lang xml name for later
$langXMLNames[$main_path] = $langCodeName . '_' . $this->activeComponentsNames[$id] ;
// we must check if old folder is found and remove it
if (!isset($this->languageTracker[$main_path]) && JFolder::exists($main_path))
{
// remove the main folder
ComponentbuilderHelper::removeFolder($main_path);
// do not remove it again
$this->languageTracker[$main_path] = true;
}
// check if exist and create if not
if (!JFolder::exists($main_path))
{
JFolder::create($main_path);
// count the folder created
$this->folderCount++;
}
foreach ($areas as $area => $languageStrings)
{
// get the file name
$fileName = $this->getLanguageFileName($file_name, $tag, $area);
// check if language should be added
if ($this->shouldLanguageBeAdded($tag, $languageStrings, $mainLangLoader[$area], $fileName) && ($actions = $this->getLangActions($file_name, $tag, $area, $target_client)) !== false)
{
// set the language data
$lang = array_map(
function ($langstring, $placeholder) {
return $placeholder . '="' . $langstring . '"';
}, array_values($languageStrings),
array_keys($languageStrings)
);
// set the line counter
$this->lineCount = $this->lineCount + count(
(array) $lang
);
// check that the main folder exist
foreach ($actions as $act)
{
$client_path = $main_path . $act['target_client'] . '/';
// check if exist and create if not
if (!JFolder::exists($client_path))
{
JFolder::create($client_path);
// count the folder created
$this->folderCount++;
}
// write the language data to a file
ComponentbuilderHelper::writeFile(
$client_path . $act['file_name'], implode(PHP_EOL, $lang)
);
// count the file created
$this->fileCount++;
// build xml strings
if (!isset($langXML[$main_path]))
{
$langXML[$main_path] = array();
$langPackages[$main_path] = array();
}
if (!isset($langXML[$main_path][$act['target_client']]))
{
$langXML[$main_path][$act['target_client']] = array();
}
// set the package targets
$langPackages[$main_path][$act['target_client']] = $act['target'];
$langXML[$main_path][$act['target_client']][] = $act['file_name'];
}
// clear memory
unset($lang);
}
}
}
}
}
// load the lang xml
if (ComponentbuilderHelper::checkArray($langXML))
{
foreach ($langXML as $main_path => $target_clients)
{
// get the XML
$xml = str_replace(
array_keys($this->fileContentStatic),
array_values($this->fileContentStatic),
$this->getLanguageXML($target_clients, $langPackages[$main_path], $langNames[$main_path])
);
// get the XML File Name
$xmlFileName = $langXMLNames[$main_path] . '.xml';
// write the language data to a file
ComponentbuilderHelper::writeFile(
$main_path . $xmlFileName, $xml
);
// set the zip full path
$zipPath = $this->tempPath . '/' . $langZIPNames[$main_path] . '.zip';
// now zip the package
if (ComponentbuilderHelper::zip(
$main_path, $zipPath
))
{
// now remove the package
ComponentbuilderHelper::removeFolder($main_path);
}
}
}
}
}
/**
* get the language xml
*
* @return string
*
*/
protected function getLanguageXML(&$target_clients, &$targets, &$language)
{
$xml = '<?xml version="1.0" encoding="utf-8"?>';
$xml .= PHP_EOL . '<extension type="file" version="3.2" method="upgrade">';
$xml .= PHP_EOL . $this->_t(1) . '<name>' . $this->hhh . 'Component' . $this->hhh . ' - ' . $language . ' Language Pack</name>';
$xml .= PHP_EOL . $this->_t(1) . '<creationDate>' . $this->hhh . 'BUILDDATE' . $this->hhh . '</creationDate>';
$xml .= PHP_EOL . $this->_t(1) . '<author>' . $this->hhh . 'AUTHOR' . $this->hhh . '</author>';
$xml .= PHP_EOL . $this->_t(1) . '<authorEmail>' . $this->hhh . 'AUTHOREMAIL' . $this->hhh . '</authorEmail>';
$xml .= PHP_EOL . $this->_t(1) . '<authorUrl>' . $this->hhh . 'AUTHORWEBSITE' . $this->hhh . '</authorUrl>';
$xml .= PHP_EOL . $this->_t(1) . '<copyright>' . $this->hhh . 'COPYRIGHT' . $this->hhh . '</copyright>';
$xml .= PHP_EOL . $this->_t(1) . '<license>' . $this->hhh . 'LICENSE' . $this->hhh . '</license>';
$xml .= PHP_EOL . $this->_t(1) . '<version>' . $this->hhh . 'ACTUALVERSION' . $this->hhh . '</version>';
$xml .= PHP_EOL . $this->_t(1) . '<description>' . $language . ' Language Pack - ' . $this->hhh . 'SHORT_DESCRIPTION' . $this->hhh . '</description>';
$xml .= PHP_EOL . $this->_t(1) . '<fileset>';
foreach ($target_clients as $target_client => $files)
{
$xml .= PHP_EOL . $this->_t(2) . '<files folder="' . $target_client . '" target="' . $targets[$target_client] . '">';
foreach ($files as $file)
{
$xml .= PHP_EOL . $this->_t(3) . '<filename>' . $file . '</filename>';
}
$xml .= PHP_EOL . $this->_t(2) . '</files>';
}
$xml .= PHP_EOL . $this->_t(1) . '</fileset>';
$xml .= PHP_EOL . '</extension>';
return $xml;
}
/**
* get the language name
*
* @return string
*
*/
protected function getLanguageName(&$tag)
{
if (!isset($this->languageNames[$tag]))
{
if (($name = ComponentbuilderHelper::getVar('language', $tag, 'langtag', 'name')) !== false)
{
$this->languageNames[$tag] = $name;
}
else
{
$this->languageNames[$tag] = $tag;
}
}
return $this->languageNames[$tag];
}
/**
* get the language actions
*
* @return array
*
*/
protected function getLangActions(&$file_name, &$tag, &$area, &$target_client)
{
// component extention type
if (strpos($file_name, 'com_') !== false)
{
$target_client = 'admin';
$target = 'administrator/language/';
if (strpos($area, 'site') !== false)
{
$target_client = 'site';
$target = 'language/';
}
return array(
array(
'target_client' => $target_client,
'target' => $target . $tag,
'file_name' => $this->getLanguageFileName($file_name, $tag, $area)
)
);
}
elseif ('admin' === $target_client)
{
$target = 'administrator/language/';
}
else
{
$target = 'language/';
}
// module/plugin extension type (TODO we return both for now)
return array(
array(
'target_client' => $target_client,
'target' => $target . $tag,
'file_name' => $this->getLanguageFileName($file_name, $tag, $area)
),
array(
'target_client' => $target_client,
'target' => $target . $tag,
'file_name' => $this->getLanguageFileName($file_name, $tag, $area, '.sys')
)
);
}
/**
* get the language file name
*
* @return string
*
*/
protected function getLanguageFileName(&$file_name, &$tag, &$area, $type = '')
{
// component extension type
if (strpos($file_name, 'com_') !== false)
{
if (strpos($area, 'sys') !== false)
{
$type = '.sys';
}
}
// file name
return $tag . '.' . $file_name . $type . '.ini';
}
/**
* check if a translation should be added
*
* @return bool
*
*/
protected function shouldLanguageBeAdded(&$tag, &$languageStrings, &$total,
&$file_name
) {
// only log messages for none $this->langTag translations
if ($this->langTag !== $tag)
{
$langStringNr = count($languageStrings);
$langStringSum = ComponentbuilderHelper::bcmath(
'mul', $langStringNr, 100
);
$percentage = ComponentbuilderHelper::bcmath(
'div', $langStringSum, $total
);
$stringNAme = ($langStringNr == 1) ? '(string '
. $tag . ' translated)'
: '(strings ' . $tag . ' translated)';
// force load if debug lines are added
if (!$this->debugLinenr)
{
// check if we should install this translation
if ($percentage < $this->percentageLanguageAdd)
{
// dont add
$this->langNot[$file_name] = '<b>'
. $total . '</b>(total '
. $this->langTag . ' strings) only <b>'
. $langStringNr . '</b>' . $stringNAme
. ' = ' . $percentage;
return false;
}
}
// show if it was added as well
$this->langSet[$file_name] = '<b>'
. $total . '</b>(total '
. $this->langTag . ' strings) and <b>'
. $langStringNr . '</b>' . $stringNAme . ' = '
. $percentage;
}
return true;
}
/**
* Set the line number in comments
*
* @param int $nr The line number
*
* @return void
*
*/
protected function setLine($nr)
{
if ($this->debugLinenr)
{
return ' [Plugin-Language-Packaging ' . $nr . ']';
}
return '';
}
/**
* Set the tab/space
*
* @param int $nr The number of tag/space
*
* @return string
*
*/
protected function _t($nr)
{
// use global method for conformity
return ComponentbuilderHelper::_t($nr);
}
}