/** * Get Meta Info from language translation file content. * @param mixed The contents of the file using file() or get_file_contents(). * @param array A blank array, strings will be returned by association * @param array Optional associative array of reference strings * @return array The Meta Info in an array */ function getINIMeta($content, &$strings, $ref_strings = null) { // convert a string to an array if (is_string($content)) { $content = explode("\n", $content, 10); } else { if (!is_array($content)) { $content = array(); } } // look for a Byte-Order-Marker at the start of the file $file['bom'] = 'UTF-8'; if ($content) { $bom = strtolower(bin2hex(substr($content[0], 0, 4))); if ($bom == '0000feff') { $file['bom'] = 'UTF-32 BE'; } else { if ($bom == 'feff0000') { $file['bom'] = 'UTF-32 LE'; } else { if (substr($bom, 0, 4) == 'feff') { $file['bom'] = 'UTF-16 BE'; } else { if (substr($bom, 0, 4) == 'fffe') { $file['bom'] = 'UTF-16 LE'; } } } } } // parse the top line from one of these two formats // # $Id: en-GB.mod_poll.ini 6167 2007-01-04 01:16:01Z eddiea $ // # version 1.5.0 2007-01-25 10:40:16 ~0 +0 if (strpos($content[0], '.ini')) { $line = preg_replace('/^.*[.]ini[ ]+/', '', $content[0]); list($file['version'], $file['date'], $file['time'], $file['owner'], $file['complete']) = explode(' ', $line . ' ', 6); $file['headertype'] = 1; } else { $line = preg_replace('/^.*version/i', '', $content[0]); $line = trim($line); list($file['version'], $file['date'], $file['time'], $file['complete']) = explode(' ', $line . ' ', 5); $file['owner'] = ''; $file['headertype'] = 2; } // tidy up the values $file['complete'] = preg_replace('/[^0-9]/', '', $file['complete']); $file['author'] = preg_replace('/^.*author[ ]+/i', '', trim($content[1], '# ')); $file['copyright'] = preg_replace('/^.*copyright[ ]+/i', '', trim($content[2], '# ')); $file['license'] = preg_replace('/^.*license[ ]+/i', '', trim($content[3], '# ')); // parse the strings in the file into an associative array $strings = array(); foreach ($content as $line) { $line = trim($line); // 1: skip comments and blanks // 2: get the ucase key and value if (empty($line) || $line[0] == '#' || $line[0] == ';') { continue; } else { if (strpos($line, '=')) { list($key, $value) = explode('=', $line, 2); $key = strtoupper($key); $strings[$key] = $value; } } } // get the status compared to the ref strings $file = array_merge($file, TranslationsHelper::getINIstatus($ref_strings, $strings)); // set a complete flag if ($file['complete'] == $file['unchanged'] && $file['missing'] == 0) { $file['status'] = 100; } // return return $file; }
/** * Create Edit or Save a Translation File */ function edit() { // import file functions jimport('joomla.filesystem.file'); // variables global $mainframe; $options =& $this->getOptions(); // build the search highlight array $options['filter_search'] = $mainframe->getUserStateFromRequest('com_translationsmanager.files.filter_search', 'filter_search', ''); // we are creating a new file // always in the reference language if ($options['task'] == 'add') { $options['newprocess'] = 1; if (!$options['isReference']) { $mainframe->enqueueMessage(JText::_('Always create in reference language')); } } // we are in the process of creating a new file // the filename is set by the 'newfilename' field if ($options['newprocess']) { $options['newfilename'] = strtolower(JRequest::getVar('newfilename', '', '', 'string')); // validate the filename if ($options['newfilename']) { // strip off ini if (substr($options['newfilename'], -4) == '.ini') { $options['newfilename'] = substr($options['newfilename'], 0, -4); } // strip off language if (preg_match('/^[a-z]{2}-[a-z]{2}[.].*/', $options['newfilename'])) { $options['newfilename'] = substr($options['newfilename'], 6); } // set variables $options['filename'] = $options['lang'] . '.' . $options['newfilename'] . '.ini'; } else { // report error $mainframe->enqueueMessage(JText::_('filename desc')); // change task if ($options['task'] != 'add') { $options['task'] = 'edit'; $options['field_error_list']['filename'] = JText::_('filename'); } } } else { if (empty($options['filename'])) { $mainframe->enqueueMessage(JText::_('You must select a file to edit')); $mainframe->redirect('index.php?option=com_translationsmanager&task=files'); } else { if ($content = @file_get_contents($options['langPath'] . DS . 'chk.' . $options['filename'])) { list($timestamp, $userid, $username) = explode('#', $content . '##'); $user =& JFactory::getUser(); // validate the checkout if (time() - $timestamp < 3600 && $userid != 0 && $userid != $user->get('id', '0')) { // report and redirect $checkin = '<a href="index.php?option=com_translationsmanager&task=checkin&id=' . $options['filename'] . '" title="' . JText::_('Force Checkin') . '" style="font-size:smaller">[' . JText::_('Checkin') . ']</a>'; $mainframe->enqueueMessage(sprintf(JText::_('checked out by'), $options['filename'], $username, $checkin)); $mainframe->redirect('index.php?option=com_translationsmanager&task=files'); } } } } // set the reference language filename from the selected filename $options['refFilename'] = str_replace($options['lang'], $options['refLang'], $options['filename']); // find the published reference language file // default to an unpublished reference file if (JFile::exists($options['refLangPath'] . DS . $options['refFilename'])) { $options['ref_path_file'] = $options['refLangPath'] . DS . $options['refFilename']; } else { $options['ref_path_file'] = $options['refLangPath'] . DS . 'xx.' . $options['refFilename']; } // find the published selected language file // default to an unpublished new file if (JFile::exists($options['langPath'] . DS . $options['filename'])) { $options['path_file'] = $options['langPath'] . DS . $options['filename']; } else { $options['path_file'] = $options['langPath'] . DS . 'xx.' . $options['filename']; } // STRINGS: initialise $editData from the reference language file contents // $editData is an analogue of the reference file // header lines are skipped // comments and blank lines are strings with an integer index // key=value pairs are arrays with the key as an index $editData = array(); $header = 0; $refStrings = array(); if ($refContent = @file($options['ref_path_file'])) { foreach ($refContent as $k => $v) { $v = trim($v); // grab the comments (but skip up to 6 lines before we have any strings in the file) // grab the strings if (empty($v) || $v[0] == '#' || $v[0] == ';') { if ($header++ > 6) { $editData[$k] = $v; } } else { if (strpos($v, '=')) { $header = 7; list($key, $value) = explode('=', $v, 2); $key = strtoupper($key); $refStrings[$key] = $value; $editData[$key] = array('ref' => $value, 'edit' => $value); if ($options['isReference']) { $editData[$key]['lang_file'] = $value; } } } } } // STRINGS: load the selected file contents and process into $editData // only when the selected language is not the same as the reference language if ($options['isReference']) { $fileContent = $refContent; $fileStrings = array(); $fileMeta = TranslationsHelper::getINIMeta($fileContent, $fileStrings); $editStrings = $fileStrings; } else { if ($fileContent = @file($options['path_file'])) { $fileStrings = array(); $fileMeta = TranslationsHelper::getINIMeta($fileContent, $fileStrings); $editStrings = $fileStrings; foreach ($fileStrings as $k => $v) { $editData[$k]['edit'] = $v; $editData[$k]['lang_file'] = $v; } } else { $fileContent = array(); $fileStrings = array(); $fileMeta = array('headertype' => 1, 'owner' => 'ff', 'complete' => 0); $editStrings = array(); } } // STRINGS: load the user form contents and process into $editData $editFormOnly = array(); if ($FormKeys = JRequest::getVar('ffKeys', array(), '', 'ARRAY', JREQUEST_ALLOWRAW)) { $FormValues = JRequest::getVar('ffValues', array(), '', 'ARRAY', JREQUEST_ALLOWRAW); // process each key=value pair from the form into $editData foreach ($FormKeys as $k => $v) { if ($v && isset($FormValues[$k])) { $key = strtoupper(trim(stripslashes($v))); $value = trim(stripslashes(str_replace('\\n', "\n", $FormValues[$k]))); $editStrings[$key] = $value; $editData[$key]['edit'] = $value; $editData[$key]['user_form'] = $value; } } // every element of $editData must have a form entry foreach ($editData as $k => $v) { if (is_array($v) && !isset($v['user_form'])) { unset($editStrings[$k]); unset($editData[$k]); } } } // META: get the XML and status meta then initialise $options['XMLmeta'] = TranslationsHelper::getXMLMeta($options['langPath'] . DS . $options['lang'] . '.xml'); $statusMeta = TranslationsHelper::getINIstatus($refStrings, $editStrings); $editMeta = array_merge($options['XMLmeta'], $fileMeta, $statusMeta); $editMeta['filename'] = $options['filename']; // META: apply any user form values foreach ($editMeta as $k => $v) { $editMeta[$k] = JRequest::getVar($k, $v, '', 'string'); } // META: require meta values foreach (array('version', 'author') as $v) { if (empty($editMeta[$v])) { $options['field_error_list'][$v] = JText::_($v); } } // ERRORS: report any errors and change the task if (!empty($options['field_error_list']) && $options['task'] != 'add') { $mainframe->enqueueMessage(sprintf(JText::_('Values Required'), implode(', ', $options['field_error_list']))); $options['task'] = 'edit'; } // create a new file or save an existing file if ($options['task'] == 'apply' || $options['task'] == 'save') { // ensure the file does not already exist when we are creating a new file if ($options['newprocess'] && JFile::exists($options['path_file'])) { // report error and set task flag $mainframe->enqueueMessage(sprintf(JText::_('Language INI Exists'), $options['newfilename'])); $options['task'] = 'edit'; } else { // check the complete status // we set the complete value to the number of strings that are 'unchanged' // so that if the reference INI file should change the 'complete' flag is unset/broken $editMeta['complete'] = JRequest::getVar('complete', '', 'post', 'string'); $editMeta['complete'] = $editMeta['complete'] == 'COMPLETE' ? $editMeta['unchanged'] : 0; // build the header if ($editMeta['headertype'] == 1) { $saveContent = '# $Id: ' . $options['filename'] . ' ' . $editMeta['version'] . ' ' . date('Y-m-d H:i:s') . ' ' . $editMeta['owner'] . ' ~' . $editMeta['complete'] . ' $'; } else { $saveContent = '# version ' . $editMeta['version'] . ' ' . date('Y-m-d H:i:s') . ' ~' . $editMeta['complete']; } $saveContent .= "\n" . '# author ' . $editMeta['author']; $saveContent .= "\n" . '# copyright ' . $editMeta['copyright']; $saveContent .= "\n" . '# license ' . $editMeta['license']; $saveContent .= "\n\n" . '# Note : All ini files need to be saved as UTF-8'; $saveContent .= "\n\n"; // process the $editData array to get the remaining content $changedStrings = array(); $header = 0; foreach ($editData as $k => $v) { // 1: add a blank line or comment // 2: add a key=value line (no need to addslashes on quote marks) if (!is_array($v)) { $saveContent .= $v . "\n"; } else { // change newlines in the value $value = preg_replace('/(\\r\\n)|(\\n\\r)|(\\n)/', '\\n', $v['edit']); // change single-quotes or backticks in the value if ($options['backticks'] > 0) { $value = strtr($value, "'", '`'); } else { if ($options['backticks'] < 0) { $value = strtr($value, '`', "'"); } } // set back to $editData $editData[$k]['edit'] = $value; // add to file content $saveContent .= $k . '=' . $value . "\n"; // if the string is in the selected language file if (isset($v['lang_file'])) { // and it has changed (via the user form) if ($v['lang_file'] != $v['edit']) { // log the change in a translation array $changedStrings["\n" . $k . '=' . $v['lang_file']] = "\n" . $k . '=' . $v['edit']; } } } } // if there is no reference Language File, automatically initialise/create one which is the same as the selected language file if ($options['refLangMissing']) { if (JFile::write($options['refLangPath'] . DS . $options['refLangFile'], trim($saveContent))) { $mainframe->enqueueMessage(sprintf(JText::_('Language INI Created'), $options['refLangFile'])); } } // 1: write the selected language file and clear newprocess flag // 2: report failure if (JFile::write($options['path_file'], trim($saveContent))) { $mainframe->enqueueMessage(sprintf(JText::_('Language INI ' . ($options['newprocess'] ? 'Created' : 'Saved')), $options['clientName'], $options['filename'])); $options['newprocess'] = 0; } else { $mainframe->enqueueMessage(sprintf(JText::_('Could not write to file'), $options['path_file'])); } // process changed strings globally across all the the ini files from the selected language directory if (count($changedStrings) && $options['globalChanges']) { $write = 0; $writeFiles = array(); if ($files = JFolder::files($options['langPath'])) { foreach ($files as $file) { // skip non-INI files // skip this file // skip this file (unpublished) // skip checked out files if (strtolower(substr($file, -4) != '.ini') || $file == $options['filename'] || $file == 'xx.' . $options['filename'] || array_search($options['langPath'] . DS . 'chk.' . $file, $files)) { continue; } // otherwise grab the file content if ($content = file_get_contents($options['langPath'] . DS . $file)) { // parse the changed strings $new_content = strtr($content, $changedStrings); // check for changes then write to the file if ($new_content != $content) { if (JFile::write($options['langPath'] . DS . $file, trim($new_content))) { $writeFiles[$write++] = $file; } } } } } // report if ($write) { $mainframe->enqueueMessage(sprintf(JText::_('Global String Change'), $write, implode('; ', $writeFiles))); } } } } // 1: checkin when we are saving (this will redirect also) // 2: call the html when we are editing or applying (and checkout existing files) if ($options['task'] == 'save') { $this->_multitask('checkin', $options['filename'], 'files', false); } else { $view = $this->getView($this->_name, 'html'); $view->setLayout('edit'); $view->assignRef('data', $editData); $view->assignRef('meta', $editMeta); $view->assignRef('options', $options); $view->display(); if (!$options['newprocess']) { $this->_multitask('checkout', $options['filename'], false, false); } } }