@@ -17,6 +17,7 @@ import org.cobbzilla.wizard.filters.auth.TokenPrincipal; | |||
import org.cobbzilla.wizard.model.HashedPassword; | |||
import org.cobbzilla.wizard.model.Identifiable; | |||
import org.cobbzilla.wizard.model.IdentifiableBase; | |||
import org.cobbzilla.wizard.model.entityconfig.EntityFieldType; | |||
import org.cobbzilla.wizard.model.entityconfig.annotations.*; | |||
import org.cobbzilla.wizard.model.search.SqlViewSearchResult; | |||
import org.cobbzilla.wizard.validation.ConstraintViolationBean; | |||
@@ -33,6 +34,7 @@ import java.util.Arrays; | |||
import java.util.List; | |||
import java.util.regex.Pattern; | |||
import static bubble.server.BubbleConfiguration.DEFAULT_LOCALE; | |||
import static java.util.concurrent.TimeUnit.MILLISECONDS; | |||
import static java.util.concurrent.TimeUnit.SECONDS; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.*; | |||
@@ -110,6 +112,11 @@ public class Account extends IdentifiableBase implements TokenPrincipal, SqlView | |||
@Type(type=ENCRYPTED_STRING) @Column(columnDefinition="varchar("+(10000+ENC_PAD)+")") | |||
@Getter @Setter private String description; | |||
@ECSearchable @ECField(type=EntityFieldType.locale) | |||
@Size(max=20, message="err.locale.length") | |||
@Type(type=ENCRYPTED_STRING) @Column(columnDefinition="varchar("+(20+ENC_PAD)+") NOT NULL") | |||
@Getter @Setter private String locale = DEFAULT_LOCALE; | |||
@ECSearchable | |||
@Getter @Setter private Boolean admin = false; | |||
public boolean admin () { return admin != null && admin; } | |||
@@ -3,6 +3,7 @@ package bubble.model.app; | |||
import bubble.model.account.Account; | |||
import bubble.model.account.AccountTemplate; | |||
import bubble.rule.AppRuleDriver; | |||
import bubble.server.BubbleConfiguration; | |||
import com.fasterxml.jackson.annotation.JsonIgnore; | |||
import com.fasterxml.jackson.databind.JsonNode; | |||
import lombok.Getter; | |||
@@ -21,6 +22,7 @@ import javax.persistence.*; | |||
import java.util.Locale; | |||
import static bubble.ApiConstants.*; | |||
import static bubble.server.BubbleConfiguration.DEFAULT_LOCALE; | |||
import static org.cobbzilla.util.io.StreamUtil.stream2string; | |||
import static org.cobbzilla.util.json.JsonUtil.json; | |||
import static org.cobbzilla.util.reflect.ReflectionUtil.copy; | |||
@@ -98,7 +100,7 @@ public class RuleDriver extends IdentifiableBase implements AccountTemplate { | |||
@Transient @Getter @Setter private AppRuleDriverDescriptor descriptor; | |||
public AppRuleDriverDescriptor getDescriptor(Locale locale) { | |||
final String localeString = locale != null ? locale.toString() : "en_US"; | |||
final String localeString = locale != null ? locale.toString() : DEFAULT_LOCALE; | |||
final AppRuleDriverDescriptor localized; | |||
final String prefix = driverClass.replace(".", "/"); | |||
try { | |||
@@ -28,6 +28,7 @@ import static bubble.ApiConstants.EP_NETWORKS; | |||
import static bubble.ApiConstants.ROOT_NETWORK_UUID; | |||
import static bubble.model.cloud.BubbleDomain.DOMAIN_NAME_MAXLEN; | |||
import static bubble.model.cloud.BubbleNetworkState.created; | |||
import static bubble.server.BubbleConfiguration.DEFAULT_LOCALE; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.die; | |||
import static org.cobbzilla.util.reflect.ReflectionUtil.copy; | |||
import static org.cobbzilla.wizard.model.crypto.EncryptedTypes.ENCRYPTED_STRING; | |||
@@ -107,7 +108,7 @@ public class BubbleNetwork extends IdentifiableBase implements HasNetwork, HasBu | |||
@ECSearchable @ECField(type=EntityFieldType.locale) | |||
@Size(max=20, message="err.locale.length") | |||
@Type(type=ENCRYPTED_STRING) @Column(columnDefinition="varchar("+(20+ENC_PAD)+") NOT NULL") | |||
@Getter @Setter private String locale = "en_US"; | |||
@Getter @Setter private String locale = DEFAULT_LOCALE; | |||
// A unicode timezone alias from: cobbzilla-utils/src/main/resources/org/cobbzilla/util/time/unicode-timezones.xml | |||
// All unicode aliases are guaranteed to map to a Linux timezone and a Java timezone | |||
@@ -23,6 +23,7 @@ import bubble.service.cloud.StandardNetworkService; | |||
import com.fasterxml.jackson.databind.JsonNode; | |||
import lombok.Cleanup; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.cobbzilla.util.string.LocaleUtil; | |||
import org.cobbzilla.wizard.auth.ChangePasswordRequest; | |||
import org.cobbzilla.wizard.client.ApiClientBase; | |||
import org.cobbzilla.wizard.client.script.ApiRunner; | |||
@@ -39,6 +40,7 @@ import javax.ws.rs.*; | |||
import javax.ws.rs.core.Context; | |||
import javax.ws.rs.core.Response; | |||
import java.io.StringWriter; | |||
import java.util.Locale; | |||
import static bubble.ApiConstants.*; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.errorString; | |||
@@ -69,6 +71,28 @@ public class MeResource { | |||
} | |||
} | |||
@GET @Path(EP_LOCALE) | |||
public Response getLocale(@Context ContainerRequest ctx) { | |||
final Account account = userPrincipal(ctx); | |||
return ok(account.getLocale()); | |||
} | |||
@POST @Path(EP_LOCALE+"/{locale}") | |||
public Response setLocale(@Context ContainerRequest ctx, | |||
@PathParam("locale") String locale) { | |||
final Account account = userPrincipal(ctx); | |||
final Account me = accountDAO.findByUuid(account.getUuid()); | |||
if (me == null) return notFound(); | |||
final Locale loc; | |||
try { | |||
loc = LocaleUtil.fromStringOrDie(locale); // must be valid | |||
} catch (Exception e) { | |||
return invalid("err.locale.invalid", "Invalid locale: "+locale, locale); | |||
} | |||
return ok(accountDAO.update(me.setLocale(loc.toString()))); | |||
} | |||
@POST @Path(EP_CHANGE_PASSWORD) | |||
public Response changePassword(@Context ContainerRequest ctx, | |||
ChangePasswordRequest request) { | |||
@@ -65,6 +65,7 @@ public class BubbleConfiguration extends PgRestServerConfiguration | |||
public static final String TAG_PAYMENTS_ENABLED = "paymentsEnabled"; | |||
public static final String TAG_CLOUD_DRIVERS = "cloudDrivers"; | |||
public static final String TAG_ENTITY_CLASSES = "entityClasses"; | |||
public static final String TAG_LOCALES = "locales"; | |||
public static final String DEFAULT_LOCALE = "en_US"; | |||
public static final String DEFAULT_LOCAL_STORAGE_DIR = HOME_DIR + "/.bubble_local_storage"; | |||
@@ -237,12 +238,15 @@ public class BubbleConfiguration extends PgRestServerConfiguration | |||
{TAG_SAGE_LAUNCHER, isSageLauncher()}, | |||
{TAG_PAYMENTS_ENABLED, paymentsEnabled()}, | |||
{TAG_CLOUD_DRIVERS, getCloudDriverClasses()}, | |||
{TAG_ENTITY_CLASSES, getSortedEntityClassNames()} | |||
{TAG_ENTITY_CLASSES, getSortedEntityClassNames()}, | |||
{TAG_LOCALES, getAllLocales()} | |||
})); | |||
} | |||
return publicSystemConfigs.get(); | |||
} | |||
} | |||
// called after activation, because now thisNetwork will be defined. otherwise it remains unchanged | |||
public void refreshPublicSystemConfigs () { synchronized (publicSystemConfigs) { publicSystemConfigs.set(null); } } | |||
@Getter @Setter private String[] disallowedCountries; | |||
@@ -153,8 +153,6 @@ message_no_verified_contacts_subtext=Before creating your first Bubble, please v | |||
form_title_new_network=New Bubble | |||
field_label_network_name=Name | |||
field_label_network_domain=Domain | |||
field_label_locale=Language | |||
field_label_timezone=Time Zone | |||
field_label_plan=Plan | |||
field_label_region=Location | |||
field_label_footprint=Footprint | |||
@@ -180,10 +178,6 @@ footprint_description_EU=Your Bubble will only run within European Union countri | |||
footprint_name_Worldwide=World-wide | |||
footprint_description_Worldwide=Your Bubble can run anywhere in the world | |||
# Supported locales | |||
locale_codes=en_US | |||
locale_en_US=English (US) | |||
# Payment methods | |||
payment_description_credit=Credit or Debit Card | |||
payment_description_code=Invitation Code | |||
@@ -330,8 +324,6 @@ err.label.length=Label is too long | |||
err.latlon.invalid=lat/lon is invalid | |||
err.list.failed=listing failed for prefix | |||
err.listNext.failed=listing next batch failed for id | |||
err.locale.length=Locale is too long | |||
err.locale.required=Locale is required | |||
err.name.invalid=Name is invalid | |||
err.name.regexFailed=Name must start with a letter and can only contain letters, numbers, hyphens, periods and underscores | |||
err.node.name.alreadyExists=A node already exists with the same FQDN | |||
@@ -434,9 +426,6 @@ err.tgzB64.invalid.missingTasksMainYml=No tasks/main.yml file found for role in | |||
err.tgzB64.invalid.writingToStorage=Error writing tgz to storage | |||
err.tgzB64.invalid.readingFromStorage=Error reading tgz from storage | |||
err.tgzB64.required=tgzB64 is required | |||
err.timezone.unknown=An error ocurred trying to determine the time zone | |||
err.timezone.length=Time zone is too long | |||
err.timezone.required=Time zone is requird | |||
err.totpKey.length=TOTP key is required | |||
err.type.notVerifiable=Type is not verifiable | |||
err.type.invalid=Type is invalid | |||
@@ -8,6 +8,27 @@ message_false=False | |||
message_null=null | |||
message_undefined=undefined | |||
button_label_set_locale=Set Language | |||
field_label_locale=Language | |||
field_label_timezone=Time Zone | |||
# Locales | |||
locale_codes=en_US,fr_FR,es_ES,it_IT,de_DE,en_GB,es_US | |||
locale_en_US=English (US) | |||
locale_es_US=Spanish (US) | |||
locale_es_ES=Spanish | |||
locale_it_IT=Italian | |||
locale_fr_FR=French | |||
locale_de_DE=German | |||
locale_en_GB=English (GB) | |||
locale_detect=Auto Detect | |||
err.locale.invalid=Locale is invalid | |||
err.locale.length=Locale is too long | |||
err.locale.required=Locale is required | |||
err.timezone.unknown=An error occurred trying to determine the time zone | |||
err.timezone.length=Time zone is too long | |||
err.timezone.required=Time zone is required | |||
err.name.required=Name is required | |||
err.name.tooShort=Name must be at least 4 characters | |||
err.name.tooLong=Name cannot be longer than 100 characters | |||
@@ -1 +1 @@ | |||
Subproject commit 3eab904291f158ab130c549a2bdd15bb77f0ec03 | |||
Subproject commit 7426503bbf995c3ff73ec8ea33bee1af83b8d3e1 |
@@ -1 +1 @@ | |||
Subproject commit 9087017eda08a97479010705e00014d31e8ccec1 | |||
Subproject commit 7437196eaf77b7426f43264a781f9f0457ff710f |