Browse Source

Added language support in users list

Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
tags/v14.0.0beta1
John Molakvoæ (skjnldsv) 6 years ago
parent
commit
6ada8254c9
No account linked to committer's email address

+ 6
- 1
core/css/inputs.scss View File

@@ -718,6 +718,8 @@ input {
border: 1px solid nc-darken($color-main-background, 14%);
background: $color-main-background;
z-index: 50;
max-height: 250px;
overflow-y: auto;
.multiselect__content {
width: 100%;
padding: 5px 0;
@@ -750,7 +752,7 @@ input {
color: nc-lighten($color-main-text, 33%);
width: 100%;
/* selected checkmark icon */
&::before {
&:not(.multiselect__option--disabled)::before {
content: ' ';
background-image: url('../img/actions/checkmark.svg?v=1');
background-repeat: no-repeat;
@@ -762,6 +764,9 @@ input {
margin-right: 5px;
visibility: hidden;
}
&.multiselect__option--disabled {
background-color: nc-darken($color-main-background, 8%);
}
/* add the prop tag-placeholder="create" to add the +
* icon on top of an unknown-and-ready-to-be-created entry
*/

+ 78
- 3
lib/private/L10N/Factory.php View File

@@ -59,6 +59,11 @@ class Factory implements IFactory {
*/
protected $pluralFunctions = [];

const COMMON_LANGUAGE_CODES = [
'en', 'es', 'fr', 'de', 'de_DE', 'ja', 'ar', 'ru', 'nl', 'it',
'pt_BR', 'pt_PT', 'da', 'fi_FI', 'nb_NO', 'sv', 'tr', 'zh_CN', 'ko'
];

/** @var IConfig */
protected $config;

@@ -137,9 +142,9 @@ class Factory implements IFactory {
*
* @link https://github.com/owncloud/core/issues/21955
*/
if($this->config->getSystemValue('installed', false)) {
if ($this->config->getSystemValue('installed', false)) {
$userId = !is_null($this->userSession->getUser()) ? $this->userSession->getUser()->getUID() : null;
if(!is_null($userId)) {
if (!is_null($userId)) {
$userLang = $this->config->getUserValue($userId, 'core', 'lang', null);
} else {
$userLang = null;
@@ -310,7 +315,7 @@ class Factory implements IFactory {
*/
private function isSubDirectory($sub, $parent) {
// Check whether $sub contains no ".."
if(strpos($sub, '..') !== false) {
if (strpos($sub, '..') !== false) {
return false;
}

@@ -441,4 +446,74 @@ class Factory implements IFactory {
return $function;
}
}

/**
* returns the common language and other languages in an
* associative array
*
* @return array
*/
public function getLanguages() {
$forceLanguage = $this->config->getSystemValue('force_language', false);
if ($forceLanguage !== false) {
return [];
}

$languageCodes = $this->findAvailableLanguages();

$commonLanguages = [];
$languages = [];

foreach($languageCodes as $lang) {
$l = $this->get('lib', $lang);
// TRANSLATORS this is the language name for the language switcher in the personal settings and should be the localized version
$potentialName = (string) $l->t('__language_name__');
if ($l->getLanguageCode() === $lang && $potentialName[0] !== '_') {//first check if the language name is in the translation file
$ln = array(
'code' => $lang,
'name' => $potentialName
);
} else if ($lang === 'en') {
$ln = array(
'code' => $lang,
'name' => 'English (US)'
);
} else {//fallback to language code
$ln = array(
'code' => $lang,
'name' => $lang
);
}

// put appropriate languages into appropriate arrays, to print them sorted
// common languages -> divider -> other languages
if (in_array($lang, self::COMMON_LANGUAGE_CODES)) {
$commonLanguages[array_search($lang, self::COMMON_LANGUAGE_CODES)] = $ln;
} else {
$languages[] = $ln;
}
}

ksort($commonLanguages);

// sort now by displayed language not the iso-code
usort( $languages, function ($a, $b) {
if ($a['code'] === $a['name'] && $b['code'] !== $b['name']) {
// If a doesn't have a name, but b does, list b before a
return 1;
}
if ($a['code'] !== $a['name'] && $b['code'] === $b['name']) {
// If a does have a name, but b doesn't, list a before b
return -1;
}
// Otherwise compare the names
return strcmp($a['name'], $b['name']);
});

return [
// reset indexes
'commonlanguages' => array_values($commonLanguages),
'languages' => $languages
];
}
}

+ 16
- 56
lib/private/Settings/Personal/PersonalInfo.php View File

@@ -39,6 +39,7 @@ use OCP\L10N\IFactory;
use OCP\Settings\ISettings;

class PersonalInfo implements ISettings {

/** @var IConfig */
private $config;
/** @var IUserManager */
@@ -51,12 +52,6 @@ class PersonalInfo implements ISettings {
private $appManager;
/** @var IFactory */
private $l10nFactory;

const COMMON_LANGUAGE_CODES = [
'en', 'es', 'fr', 'de', 'de_DE', 'ja', 'ar', 'ru', 'nl', 'it',
'pt_BR', 'pt_PT', 'da', 'fi_FI', 'nb_NO', 'sv', 'tr', 'zh_CN', 'ko'
];

/** @var IL10N */
private $l;

@@ -198,64 +193,29 @@ class PersonalInfo implements ISettings {

$uid = $user->getUID();

$userLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage());
$languageCodes = $this->l10nFactory->findAvailableLanguages();

$commonLanguages = [];
$languages = [];
$userConfLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage());
$languages = $this->l10nFactory->getLanguages();

foreach($languageCodes as $lang) {
$l = \OC::$server->getL10N('lib', $lang);
// TRANSLATORS this is the language name for the language switcher in the personal settings and should be the localized version
$potentialName = (string) $l->t('__language_name__');
if($l->getLanguageCode() === $lang && $potentialName[0] !== '_') {//first check if the language name is in the translation file
$ln = array('code' => $lang, 'name' => $potentialName);
} elseif ($lang === 'en') {
$ln = ['code' => $lang, 'name' => 'English (US)'];
}else{//fallback to language code
$ln=array('code'=>$lang, 'name'=>$lang);
}

// put appropriate languages into appropriate arrays, to print them sorted
// used language -> common languages -> divider -> other languages
if ($lang === $userLang) {
$userLang = $ln;
} elseif (in_array($lang, self::COMMON_LANGUAGE_CODES)) {
$commonLanguages[array_search($lang, self::COMMON_LANGUAGE_CODES)]=$ln;
} else {
$languages[]=$ln;
}
// associate the user language with the proper array
$userLangIndex = array_search($userConfLang, array_column($languages['commonlanguages'], 'code'));
$userLang = $languages['commonlanguages'][$userLangIndex];
// search in the other languages
if ($userLangIndex === false) {
$userLangIndex = array_search($userConfLang, array_column($languages['languages'], 'code'));
$userLang = $languages['languages'][$userLangIndex];
}

// if user language is not available but set somehow: show the actual code as name
if (!is_array($userLang)) {
$userLang = [
'code' => $userLang,
'name' => $userLang,
'code' => $userConfLang,
'name' => $userConfLang,
];
}

ksort($commonLanguages);

// sort now by displayed language not the iso-code
usort( $languages, function ($a, $b) {
if ($a['code'] === $a['name'] && $b['code'] !== $b['name']) {
// If a doesn't have a name, but b does, list b before a
return 1;
}
if ($a['code'] !== $a['name'] && $b['code'] === $b['name']) {
// If a does have a name, but b doesn't, list a before b
return -1;
}
// Otherwise compare the names
return strcmp($a['name'], $b['name']);
});

return [
'activelanguage' => $userLang,
'commonlanguages' => $commonLanguages,
'languages' => $languages
];
return array_merge(
array('activelanguage' => $userLang),
$languages
);
}

/**

+ 6
- 0
settings/css/settings.scss View File

@@ -1305,6 +1305,12 @@ doesnotexist:-o-prefocus, .strengthify-wrapper {
.quota {
width: 170px;
}
.languages {
width: 200px;
.multiselect {
width: 100%;
}
}
.storageLocation {
width: 250px;
}

+ 13
- 38
settings/js/main.js
File diff suppressed because it is too large
View File


+ 1
- 1
settings/package.json View File

@@ -16,7 +16,7 @@
"vue-click-outside": "^1.0.7",
"vue-infinite-loading": "^2.2.3",
"vue-localstorage": "^0.6.2",
"vue-multiselect": "^2.1",
"vue-multiselect": "^2.1.0",
"vue-router": "^3.0.1",
"vuex": "^3.0.1",
"vuex-router-sync": "^5.0.0"

+ 3
- 0
settings/src/components/userList.vue View File

@@ -10,6 +10,8 @@
<div id="headerSubAdmins" class="subadmins"
v-if="subAdminsGroups.length>0">{{ t('settings', 'Group admin for') }}</div>
<div id="headerQuota" class="quota">{{ t('settings', 'Quota') }}</div>
<div id="headerLanguages" class="languages"
v-if="showConfig.showLanguages">{{ t('settings', 'Languages') }}</div>
<div class="headerStorageLocation storageLocation"
v-if="showConfig.showStoragePath">{{ t('settings', 'Storage location') }}</div>
<div class="headerUserBackend userBackend"
@@ -71,6 +73,7 @@
@tag="validateQuota" >
</multiselect>
</div>
<div class="languages" v-if="showConfig.showLanguages"></div>
<div class="storageLocation" v-if="showConfig.showStoragePath"></div>
<div class="userBackend" v-if="showConfig.showUserBackend"></div>
<div class="lastLogin" v-if="showConfig.showLastLogin"></div>

+ 59
- 4
settings/src/components/userList/userRow.vue View File

@@ -53,6 +53,15 @@
</multiselect>
<progress class="quota-user-progress" :class="{'warn':usedQuota>80}" :value="usedQuota" max="100"></progress>
</div>
<div class="languages" :class="{'icon-loading-small': loading.languages}"
v-if="showConfig.showLanguages">
<multiselect :value="userLanguage" :options="languages" :disabled="loading.languages||loading.all"
:placeholder="t('settings', 'No language set')"
label="name" track-by="code" class="multiselect-vue"
:allowEmpty="false" group-values="languages" group-label="label"
@input="setUserLanguage">
</multiselect>
</div>
<div class="storageLocation" v-if="showConfig.showStoragePath">{{user.storageLocation}}</div>
<div class="userBackend" v-if="showConfig.showUserBackend">{{user.backend}}</div>
<div class="lastLogin" v-if="showConfig.showLastLogin" :title="user.lastLogin>0 ? OC.Util.formatDate(user.lastLogin) : ''">
@@ -73,7 +82,6 @@
import popoverMenu from '../popoverMenu';
import ClickOutside from 'vue-click-outside';
import Multiselect from 'vue-multiselect';
//import Multiselect from '../../../node_modules/vue-multiselect/src/index';

export default {
name: 'userRow',
@@ -86,8 +94,9 @@ export default {
ClickOutside
},
mounted() {
// prevent click outside event with popupItem.
this.popupItem = this.$el;
// required if popup needs to stay opened after menu click
// since we only have disable/delete actions, let's close it directly
// this.popupItem = this.$el;
},
data() {
return {
@@ -102,7 +111,8 @@ export default {
subadmins: false,
quota: false,
delete: false,
disable: false
disable: false,
languages: false
}
}
},
@@ -159,6 +169,33 @@ export default {
/* PASSWORD POLICY? */
minPasswordLength() {
return this.$store.getters.getPasswordPolicyMinLength;
},

/* LANGUAGES */
languages() {
return Array(
{
label: t('settings', 'Common languages'),
languages: this.settings.languages.commonlanguages
},
{
label: t('settings', 'All languages'),
languages: this.settings.languages.languages
}
);
},
userLanguage() {
let availableLanguages = this.languages[0].languages.concat(this.languages[1].languages);
let userLang = availableLanguages.find(lang => lang.code === this.user.language);
if (typeof userLang !== 'object' && this.user.language !== '') {
return {
code: this.user.language,
name: this.user.language
}
} else if(this.user.language === '') {
return false;
}
return userLang;
}
},
methods: {
@@ -370,6 +407,24 @@ export default {
return quota;
},


/**
* Validate quota string to make sure it's a valid human file size
*
* @param {string|Object} quota Quota in readable format '5 GB' or Object {id: '5 GB', label: '5GB'}
* @returns {string}
*/
setUserLanguage(lang) {
this.loading.languages = true;
// ensure we only send the preset id
this.$store.dispatch('setUserData', {
userid: this.user.id,
key: 'language',
value: lang.code
}).then(() => this.loading.languages = false);
return lang;
},

/**
* Validate quota string to make sure it's a valid human file size
*

+ 4
- 3
settings/src/store/users.js View File

@@ -397,12 +397,13 @@ const actions = {
* @returns {Promise}
*/
setUserData(context, { userid, key, value }) {
if (['email', 'quota', 'displayname', 'password'].indexOf(key) !== -1) {
let allowedEmpty = ['email', 'displayname'];
if (['email', 'language', 'quota', 'displayname', 'password'].indexOf(key) !== -1) {
// We allow empty email or displayname
if (typeof value === 'string' &&
(
(['quota', 'password'].indexOf(key) !== -1 && value.length > 0) ||
['email', 'displayname'].indexOf(key) !== -1
(allowedEmpty.indexOf(key) === -1 && value.length > 0) ||
allowedEmpty.indexOf(key) !== -1
)
) {
return api.requireAdmin().then((response) => {

+ 17
- 4
settings/src/views/Users.vue View File

@@ -2,6 +2,11 @@
<div id="app">
<app-navigation :menu="menu">
<template slot="settings-content">
<div>
<input type="checkbox" id="showLanguages" class="checkbox"
:checked="showLanguages" v-model="showLanguages">
<label for="showLanguages">{{t('settings', 'Show Languages')}}</label>
</div>
<div>
<input type="checkbox" id="showLastLogin" class="checkbox"
:checked="showLastLogin" v-model="showLastLogin">
@@ -50,7 +55,8 @@ export default {
showStoragePath: false,
showUserBackend: false,
showLastLogin: false,
showNewUserForm: false
showNewUserForm: false,
showLanguages: false
}
}
},
@@ -82,6 +88,14 @@ export default {
usersLimit() {
return this.$store.getters.getUsersLimit;
},

// Local settings
showLanguages: {
get: function() {return this.getLocalstorage('showLanguages')},
set: function(status) {
this.setLocalStorage('showLanguages', status);
}
},
showLastLogin: {
get: function() {return this.getLocalstorage('showLastLogin')},
set: function(status) {
@@ -100,6 +114,8 @@ export default {
this.setLocalStorage('showStoragePath', status);
}
},


userCount() {
return this.$store.getters.getUserCount;
},
@@ -164,6 +180,3 @@ export default {
}
}
</script>

<style lang="scss">
</style>

+ 6
- 0
settings/users.php View File

@@ -43,6 +43,7 @@ $userManager = \OC::$server->getUserManager();
$groupManager = \OC::$server->getGroupManager();
$appManager = \OC::$server->getAppManager();
$config = \OC::$server->getConfig();
$l10nFactory = \OC::$server->getL10NFactory();

/* SORT OPTION: SORT_USERCOUNT or SORT_GROUPNAME */
$sortGroupsBy = \OC\Group\MetaData::SORT_USERCOUNT;
@@ -129,6 +130,9 @@ function addition($v, $w) {
}
$userCount = array_reduce($userManager->countUsers(), 'addition', 0);

/* LANGUAGES */
$languages = $l10nFactory->getLanguages();

/* FINAL DATA */
$serverData = array();
// groups
@@ -139,6 +143,7 @@ $serverData['subadmins'] = $subAdmins;
$serverData['sortGroups'] = $sortGroupsBy;
$serverData['quotaPreset'] = $quotaPreset;
$serverData['userCount'] = $userCount-$disabledUsers;
$serverData['languages'] = $languages;
// Settings
$serverData['defaultQuota'] = $defaultQuota;
$serverData['canChangePassword'] = $canChangePassword;
@@ -147,3 +152,4 @@ $serverData['canChangePassword'] = $canChangePassword;
$tmpl = new OC_Template('settings', 'settings', 'user');
$tmpl->assign('serverData', $serverData);
$tmpl->printPage();


Loading…
Cancel
Save