Skip to content

Commit

Permalink
Merge pull request #62 from AcclaroInc/release/1.7.0
Browse files Browse the repository at this point in the history
Release/1.7.0
  • Loading branch information
sidedwards authored May 14, 2020
2 parents 7ef9e1f + 86685bb commit b3716f2
Show file tree
Hide file tree
Showing 13 changed files with 933 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .craftplugin
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"pluginName": "Translations for Craft",
"pluginDescription": "Easily launch and manage multilingual Craft websites without having to copy/paste content or manually track updates.",
"pluginVersion": "1.6.0",
"pluginVersion": "1.7.0",
"pluginAuthorName": "Acclaro",
"pluginVendorName": "Acclaro",
"pluginAuthorUrl": "http://www.acclaro.com/",
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).

## 1.7.0 - 2020-05-14

### Added
- Support for managing static translations
- Minor UI fixes

## 1.6.0 - 2020-04-13

### Added
Expand Down
20 changes: 20 additions & 0 deletions src/Translations.php
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,12 @@ public function getCpNavItem()
'url' => 'translations/translators',
];
}
if ($currentUser->can('translations:static-translations')) {
$subNavs['static-translations'] = [
'label' => 'Static Translations',
'url' => 'translations/static-translations',
];
}

if ($currentUser->can('translations:settings')) {
$subNavs['settings'] = [
Expand Down Expand Up @@ -343,6 +349,9 @@ private function _registerCpRoutes()
'translations/settings/send-logs' => 'translations/settings/send-logs',
'translations/orders/get-file-diff/<fileId:\d+>' => 'translations/base/get-file-diff',
'translations/settings/configuration-options' => 'translations/settings/configuration-options',
'translations/static-translations' => 'translations/static-translations',
'translations/static-translations/export-file' => 'translations/static-translations/export-file',
'translations/static-translations/import' => 'translations/static-translations/import',
]);
}
);
Expand Down Expand Up @@ -583,6 +592,17 @@ protected function customAdminCpPermissions(): array
]
]
],
'translations:static-translations' => [
'label' => Craft::t('translations', 'Static Translations'),
'nested' => [
'translations:static-translations:import' => [
'label' => Craft::t('translations', 'Import'),
],
'translations:static-translations:export' => [
'label' => Craft::t('translations', 'Export'),
]
]
],
'translations:orders' => [
'label' => Craft::t('translations', 'View Orders'),
'nested' => [
Expand Down
32 changes: 32 additions & 0 deletions src/assetbundles/StaticTranslationsAssets.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php
/**
* Translations for Craft plugin for Craft CMS 3.x
*
* Translations for Craft eliminates error prone and costly copy/paste workflows for launching human translated Craft CMS web content.
*
* @link http://www.acclaro.com/
* @copyright Copyright (c) 2018 Acclaro
*/

namespace acclaro\translations\assetbundles;

use craft\web\AssetBundle;
use craft\web\assets\cp\CpAsset;

class StaticTranslationsAssets extends AssetBundle
{
public function init()
{
$this->sourcePath = '@acclaro/translations/assetbundles/src';

$this->depends = [
CpAsset::class,
];

$this->js = [
'js/StaticTranslations.js',
];

parent::init();
}
}
4 changes: 4 additions & 0 deletions src/assetbundles/src/css/Translations.css
Original file line number Diff line number Diff line change
Expand Up @@ -625,3 +625,7 @@
.translations-about .link a {
padding-right: 15px;
}

.static-trans-footer .pagination {
display: none;
}
93 changes: 93 additions & 0 deletions src/assetbundles/src/js/StaticTranslations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
(function($) {

if (typeof Craft.Translations === 'undefined') {
Craft.Translations = {};
}

Craft.Translations.StaticTranslations = {

saveStaticTranslation: function() {

var data = $("#static-translation").serializeArray();
data.push(
{name: 'source', value: Craft.elementIndex.sourceKey},
{name: 'siteId', value: Craft.elementIndex.siteId}
);
Craft.postActionRequest('translations/static-translations/save', data, $.proxy(function(response, textStatus) {
if (textStatus === 'success') {
if (response.success) {
Craft.cp.displayNotice(Craft.t('app', 'Static Translations saved.'));
Craft.elementIndex.updateElements();
}
} else {
Craft.cp.displayError(Craft.t('app', 'An unknown error occurred.'));
}

$('.save-static-translation').removeClass('disabled');
$('.save-static-translation').attr("disabled", false);

}, this));


},

exportStaticTranslation: function() {

var data = {
siteId: Craft.elementIndex.siteId,
sourceKey: Craft.elementIndex.sourceKey,
search: Craft.elementIndex.searchText
};

Craft.postActionRequest('translations/static-translations/export', data, $.proxy(function(response, textStatus) {
if (textStatus === 'success') {
if (response.success) {
var $iframe = $('<iframe/>', {'src': Craft.getActionUrl('translations/static-translations/export-file', {'filename': response.filePath})}).hide();
$('#static-translation').append($iframe);
Craft.cp.displayNotice(Craft.t('app', 'Static Translations exported.'));
}
} else {
Craft.cp.displayError(Craft.t('app', 'An unknown error occurred.'));
}
}, this));

},

init: function() {
var self = this;
$('.sortmenubtn').hide();
$('.statusmenubtn').hide();
$(".sitemenubtn").appendTo("#toolbar");

$('.save-static-translation').on('click', function(e) {

$('.save-static-translation').addClass('disabled');
$('.save-static-translation').attr("disabled", true);

e.preventDefault();
console.log(Craft.elementIndex);
self.saveStaticTranslation();
});

$('#translate-export').on('click', function(e) {

e.preventDefault();
if($(".elements table:first tr").length > 1) {
self.exportStaticTranslation();
} else {
Craft.cp.displayNotice(Craft.t('app', 'No Translations to export.'));
}
});

$('.translate-import').click(function() {

$('input[name="trans-import"]').click().change(function() {
$('#siteId').val(Craft.elementIndex.siteId);
$(this).parent('form').submit();
});

});
},
};

})(jQuery);
196 changes: 196 additions & 0 deletions src/controllers/StaticTranslationsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
<?php
/**
* Translations for Craft plugin for Craft CMS 3.x
*
* Translations for Craft eliminates error prone and costly copy/paste workflows for launching human translated Craft CMS web content.
*
* @link http://www.acclaro.com/
* @copyright Copyright (c) 2018 Acclaro
*/

namespace acclaro\translations\controllers;

use Craft;
use craft\helpers\Path;
use craft\web\Controller;
use yii\web\UploadedFile;
use craft\helpers\StringHelper;
use yii\web\NotFoundHttpException;
use acclaro\translations\Translations;
use acclaro\translations\elements\StaticTranslations;

/**
* @author Acclaro
* @package Translations
* @since 1.0.0
*/
class StaticTranslationsController extends Controller
{
/**
* @return mixed
*/
public function actionIndex() {

$variables = [];
$variables['selectedSubnavItem'] = 'static-translations';

$this->requireLogin();
$this->renderTemplate('translations/static-translations/index', $variables);
}

/**
* @return \yii\web\Response
* @throws \yii\web\BadRequestHttpException
*/
public function actionSave() {

$this->requirePostRequest();

$siteId = Craft::$app->request->getRequiredBodyParam('siteId');
$site = Craft::$app->getSites()->getSiteById($siteId);
$lang = $site->language;
$translations = Craft::$app->request->getRequiredBodyParam('translation');

Translations::$plugin->staticTranslationsRepository->set($lang, $translations);

return $this->asJson([
'success' => true,
'errors' => []
]);
}

/**
* @return \yii\web\Response
* @throws \craft\errors\SiteNotFoundException
* @throws \yii\base\Exception
* @throws \yii\web\BadRequestHttpException
*/
public function actionExport() {

$this->requirePostRequest();

$siteId = Craft::$app->request->getRequiredBodyParam('siteId');
$source = Craft::$app->request->getRequiredBodyParam('sourceKey');
$source = str_replace('*', '/', $source);

$elementQuery = StaticTranslations::find();
$elementQuery->status = null;
$elementQuery->source = [$source];
$elementQuery->search = Craft::$app->request->getRequiredBodyParam('search', null);
$elementQuery->siteId = $siteId;

$translations = Translations::$plugin->staticTranslationsRepository->get($elementQuery);

$site = Craft::$app->getSites()->getSiteById($siteId);
$lang = Craft::$app->getI18n()->getLocaleById($site->language);

$primary = Craft::$app->getSites()->getPrimarySite();
$primaryLang = Craft::$app->getI18n()->getLocaleById($primary->language);
$langName = ucfirst(StringHelper::convertToUTF8($lang->displayName));

$data = '"' .Translations::$plugin->translator->translate('app', "Source: $primaryLang->displayName ($primary->language)") . '","' . Translations::$plugin->translator->translate('app', "Target: $langName ($site->language)") . "\"\r\n";
foreach ($translations as $row) {
$trans = StringHelper::convertToUTF8($row->translation);
$data .= '"' . $row->original . '","' . $trans . "\"\r\n";
}

$file = Craft::$app->getPath()->getTempPath() . DIRECTORY_SEPARATOR . 'StaticTranslations-'.$site->language.'-'.date('Ymdhis') . '.csv';
$fd = fopen($file, "w");
fputs($fd, $data);
fclose($fd);

return $this->asJson([
'success' => true,
'filePath' => $file
]);
}

/**
* Export Functionality
* Sends the csv file created to the user
*/
public function actionExportFile()
{
$filename = Craft::$app->getRequest()->getRequiredQueryParam('filename');
if (!is_file($filename) || !Path::ensurePathIsContained($filename)) {
throw new NotFoundHttpException(Craft::t('app', 'Invalid file name: {filename}', [
'filename' => $filename
]));
}

return Craft::$app->getResponse()->sendFile($filename, null, ['inline' => true]);
}

/**
* @throws \yii\web\BadRequestHttpException
*/
public function actionImport(){

$this->requireLogin();
$this->requirePostRequest();

try {

$siteId = Craft::$app->getRequest()->getRequiredBodyParam('siteId');
$site = Craft::$app->getSites()->getSiteById($siteId);

// Upload the file and drop it in the temporary folder
$file = UploadedFile::getInstanceByName('trans-import');

// validate file
if (!$this->validateFile($file)) {
Craft::$app->getSession()->setError(Craft::t('app', 'Invalid file type'));
} else {

$rows = [];
$handle = fopen($file->tempName, 'r');

while (($row = fgetcsv($handle)) !== false) {
if (isset($row[0]) && isset($row[1])) {
$rows[$row[0]] = $row[1];
}
}
fclose($handle);

if ($rows) {
Translations::$plugin->staticTranslationsRepository->set($site->language, $rows);
Craft::$app->getSession()->setNotice(Craft::t('app', 'Translations imported successfully'));
} else {
Craft::$app->getSession()->setError(Craft::t('app', 'No translation imported'));
}
}
} catch (\Exception $e) {
Craft::$app->getSession()->setError(Craft::t('app', 'Error: '.$e->getMessage()));
}

}

/**
* @param $file
* @return bool
*/
public function validateFile($file)
{
if ($file->getExtension() !== 'csv') {
return false;
}

$mimeTypes = [
'text/csv',
'text/plain',
'application/csv',
'application/txt',
'application/excel',
'application/vnd.msexcel',
'application/vnd.ms-excel',
'text/comma-separated-values',
];

if (!in_array($file->type, $mimeTypes)) {
return false;
}

return true;
}

}
Loading

0 comments on commit b3716f2

Please sign in to comment.