Commit dbcbe54c authored by Filipe de Lima Brito's avatar Filipe de Lima Brito

Add CAS login support.

parent c1871a62
...@@ -4,9 +4,11 @@ import chat.rocket.android.authentication.domain.model.TokenModel ...@@ -4,9 +4,11 @@ import chat.rocket.android.authentication.domain.model.TokenModel
import chat.rocket.android.authentication.presentation.AuthenticationNavigator import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.NetworkHelper import chat.rocket.android.helper.NetworkHelper
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.generateRandomString
import chat.rocket.android.util.extensions.isEmailValid import chat.rocket.android.util.extensions.isEmailValid
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
...@@ -41,38 +43,56 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -41,38 +43,56 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
return return
} }
view.showSignUpView(settings.registrationEnabled()) if (settings.casEnabled()) {
// Hiding the views in order to show the WebView instead.
view.hideUsernameOrEmailView()
view.hidePasswordView()
view.hideSignUpView()
view.hideOauthView()
view.hideLoginButton()
var hasSocial = false view.showLoading()
if (settings.facebookEnabled()) { val token = generateRandomString(17)
view.enableLoginByFacebook() view.showCasView(UrlHelper.getCasUrl(settings.casLoginUrl(), server, token), token)
hasSocial = true } else {
} if (settings.registrationEnabled()){
if (settings.githubEnabled()) { view.showSignUpView()
view.enableLoginByGithub() }
hasSocial = true
} var hasSocial = false
if (settings.googleEnabled()) { if (settings.facebookEnabled()) {
view.enableLoginByGoogle() view.enableLoginByFacebook()
hasSocial = true hasSocial = true
} }
if (settings.linkedinEnabled()) { if (settings.githubEnabled()) {
view.enableLoginByLinkedin() view.enableLoginByGithub()
hasSocial = true hasSocial = true
} }
if (settings.meteorEnabled()) { if (settings.googleEnabled()) {
view.enableLoginByMeteor() view.enableLoginByGoogle()
hasSocial = true hasSocial = true
} }
if (settings.twitterEnabled()) { if (settings.linkedinEnabled()) {
view.enableLoginByTwitter() view.enableLoginByLinkedin()
hasSocial = true hasSocial = true
} }
if (settings.gitlabEnabled()) { if (settings.meteorEnabled()) {
view.enableLoginByGitlab() view.enableLoginByMeteor()
hasSocial = true hasSocial = true
}
if (settings.twitterEnabled()) {
view.enableLoginByTwitter()
hasSocial = true
}
if (settings.gitlabEnabled()) {
view.enableLoginByGitlab()
hasSocial = true
}
if (hasSocial) {
view.showOauthView()
}
} }
view.showOauthView(hasSocial)
} }
fun authenticate(usernameOrEmail: String, password: String) { fun authenticate(usernameOrEmail: String, password: String) {
...@@ -110,9 +130,7 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -110,9 +130,7 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
} }
if (token != null) { if (token != null) {
val me = client.me() saveToken(server, TokenModel(token.userId, token.authToken), client.me().username)
multiServerRepository.save(server, TokenModel(token.userId, token.authToken))
localRepository.save(LocalRepository.USERNAME_KEY, me.username)
registerPushToken() registerPushToken()
navigator.toChatList() navigator.toChatList()
} else { } else {
...@@ -142,6 +160,41 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -142,6 +160,41 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
} }
} }
fun authenticateWithCas(casCredential: String) {
launchUI(strategy) {
if (NetworkHelper.hasInternetAccess()) {
view.showLoading()
try {
val server = serverInteractor.get()
if (server != null) {
val token = client.loginWithCas(casCredential)
saveToken(server, TokenModel(token.userId, token.authToken), client.me().username)
registerPushToken()
navigator.toChatList()
} else {
navigator.toServerScreen()
}
} catch (exception: RocketChatException) {
exception.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
} finally {
view.hideLoading()
}
} else {
view.showNoInternetConnection()
}
}
}
private suspend fun saveToken(server: String, tokenModel: TokenModel, username: String?) {
multiServerRepository.save(server, tokenModel)
localRepository.save(LocalRepository.USERNAME_KEY, username)
registerPushToken()
}
fun signup() = navigator.toSignUp() fun signup() = navigator.toSignUp()
private suspend fun registerPushToken() { private suspend fun registerPushToken() {
......
...@@ -6,64 +6,97 @@ import chat.rocket.android.core.behaviours.MessageView ...@@ -6,64 +6,97 @@ import chat.rocket.android.core.behaviours.MessageView
interface LoginView : LoadingView, MessageView, InternetView { interface LoginView : LoadingView, MessageView, InternetView {
/**
* Shows the CAS view if the server settings allow the sign in/sign out via CAS protocol.
* @param casUrl The CAS URL to login/sign up with.
* @param requestedToken The requested Token sent to the CAS server.
*/
fun showCasView(casUrl: String, requestedToken: String)
/**
* Shows the sign up view if the server settings allow the new users registration.
*/
fun showSignUpView()
/** /**
* Shows the oauth view if the server settings allow the login via social accounts. * Shows the oauth view if the server settings allow the login via social accounts.
* *
* REMARK: we must show at maximum *three* social accounts views ([enableLoginByFacebook], [enableLoginByGithub], [enableLoginByGoogle], * REMARK: we must show at maximum *three* social accounts views ([enableLoginByFacebook], [enableLoginByGithub], [enableLoginByGoogle],
* [enableLoginByLinkedin], [enableLoginByMeteor], [enableLoginByTwitter] or [enableLoginByGitlab]) for the oauth view. * [enableLoginByLinkedin], [enableLoginByMeteor], [enableLoginByTwitter] or [enableLoginByGitlab]) for the oauth view.
* If the possibility of login via social accounts exceeds 3 different ways we should set up the FAB ([setupFabListener]) to show the remaining view(s). * If the possibility of login via social accounts exceeds 3 different ways we should set up the FAB ([setupFabListener]) to show the remaining view(s).
*
* @param value True to show the oauth view, false otherwise.
*/ */
fun showOauthView(value: Boolean) fun showOauthView()
/** /**
* Setups the FloatingActionButton to show more social accounts views (expanding the oauth view interface to show the remaining view(s)). * Shows the login button.
*/ */
fun setupFabListener() fun showLoginButton()
/** /**
* Shows the login by Facebook view. * Shows the login by Facebook view if the server settings allow it.
*/ */
fun enableLoginByFacebook() fun enableLoginByFacebook()
/** /**
* Shows the login by Github view. * Shows the login by Github view if the server settings allow it.
*/ */
fun enableLoginByGithub() fun enableLoginByGithub()
/** /**
* Shows the login by Google view. * Shows the login by Google view if the server settings allow it.
*/ */
fun enableLoginByGoogle() fun enableLoginByGoogle()
/** /**
* Shows the login by Linkedin view. * Shows the login by Linkedin view if the server settings allow it.
*/ */
fun enableLoginByLinkedin() fun enableLoginByLinkedin()
/** /**
* Shows the login by Meteor view. * Shows the login by Meteor view if the server settings allow it.
*/ */
fun enableLoginByMeteor() fun enableLoginByMeteor()
/** /**
* Shows the login by Twitter view. * Shows the login by Twitter view if the server settings allow it.
*/ */
fun enableLoginByTwitter() fun enableLoginByTwitter()
/** /**
* Shows the login by Gitlab view. * Shows the login by Gitlab view if the server settings allow it.
*/ */
fun enableLoginByGitlab() fun enableLoginByGitlab()
/** /**
* Shows the sign up view if the server settings allow the new users registration. * Setups the FloatingActionButton to show more social accounts views (expanding the oauth view interface to show the remaining view(s)).
*
* @param value True to show the sign up view, false otherwise.
*/ */
fun showSignUpView(value: Boolean) fun setupFabListener()
/**
* Hides the username/e-mail view.
*/
fun hideUsernameOrEmailView()
/**
* Hides the password view.
*/
fun hidePasswordView()
/**
* Hides the sign up view if the server settings does not allow the new users registration.
*/
fun hideSignUpView()
/**
* Hides the oauth view.
*/
fun hideOauthView()
/**
* Hides the login button.
*/
fun hideLoginButton()
/** /**
* Alerts the user about a wrong inputted username or email. * Alerts the user about a wrong inputted username or email.
*/ */
......
package chat.rocket.android.authentication.login.ui package chat.rocket.android.authentication.login.ui
import DrawableHelper import DrawableHelper
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.graphics.Bitmap
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment import android.support.v4.app.Fragment
...@@ -10,9 +12,10 @@ import android.view.LayoutInflater ...@@ -10,9 +12,10 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.ScrollView import android.widget.ScrollView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.authentication.login.presentation.LoginPresenter import chat.rocket.android.authentication.login.presentation.LoginPresenter
import chat.rocket.android.authentication.login.presentation.LoginView import chat.rocket.android.authentication.login.presentation.LoginView
...@@ -23,7 +26,6 @@ import chat.rocket.android.util.extensions.inflate ...@@ -23,7 +26,6 @@ import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.setVisible import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.util.extensions.showToast import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent import chat.rocket.android.util.extensions.textContent
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_log_in.* import kotlinx.android.synthetic.main.fragment_authentication_log_in.*
import javax.inject.Inject import javax.inject.Inject
...@@ -47,7 +49,8 @@ class LoginFragment : Fragment(), LoginView { ...@@ -47,7 +49,8 @@ class LoginFragment : Fragment(), LoginView {
AndroidSupportInjection.inject(this) AndroidSupportInjection.inject(this)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = container?.inflate(R.layout.fragment_authentication_log_in) override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
container?.inflate(R.layout.fragment_authentication_log_in)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
...@@ -67,12 +70,6 @@ class LoginFragment : Fragment(), LoginView { ...@@ -67,12 +70,6 @@ class LoginFragment : Fragment(), LoginView {
setupSignUpListener() setupSignUpListener()
} }
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState)
areLoginOptionsNeeded()
}
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
if (isGlobalLayoutListenerSetUp) { if (isGlobalLayoutListenerSetUp) {
...@@ -81,31 +78,30 @@ class LoginFragment : Fragment(), LoginView { ...@@ -81,31 +78,30 @@ class LoginFragment : Fragment(), LoginView {
} }
} }
override fun showOauthView(value: Boolean) { override fun showCasView(casUrl: String, requestedToken: String) {
if (value) { activity?.apply {
social_accounts_container.setVisible(true) // We need to update the view headline, since the CAS allows the log in or sign up
button_fab.setVisible(true) text_headline.textContent = getString(R.string.title_log_in_or_sign_up)
setupWebView(casUrl, requestedToken)
// We need to setup the layout to hide and show the oauth interface when the soft keyboard is shown
// (means that the user touched the text_username_or_email or text_password EditText to fill that respective fields).
if (!isGlobalLayoutListenerSetUp) {
scroll_view.viewTreeObserver.addOnGlobalLayoutListener(layoutListener)
isGlobalLayoutListenerSetUp = true
}
} else {
social_accounts_container.setVisible(false)
button_fab.setVisible(false)
} }
} }
override fun setupFabListener() { override fun showSignUpView() = text_new_to_rocket_chat.setVisible(true)
button_fab.setOnClickListener({
button_fab.hide() override fun showOauthView() {
showRemainingSocialAccountsView() social_accounts_container.setVisible(true)
scrollToBottom() button_fab.setVisible(true)
})
// We need to setup the layout to hide and show the oauth interface when the soft keyboard is shown
// (means that the user touched the text_username_or_email or text_password EditText to fill that respective fields).
if (!isGlobalLayoutListenerSetUp) {
scroll_view.viewTreeObserver.addOnGlobalLayoutListener(layoutListener)
isGlobalLayoutListenerSetUp = true
}
} }
override fun showLoginButton() = button_log_in.setVisible(true)
override fun enableLoginByFacebook() { override fun enableLoginByFacebook() {
button_facebook.isEnabled = true button_facebook.isEnabled = true
} }
...@@ -134,7 +130,26 @@ class LoginFragment : Fragment(), LoginView { ...@@ -134,7 +130,26 @@ class LoginFragment : Fragment(), LoginView {
button_gitlab.isEnabled = true button_gitlab.isEnabled = true
} }
override fun showSignUpView(value: Boolean) = text_new_to_rocket_chat.setVisible(value) override fun setupFabListener() {
button_fab.setOnClickListener({
button_fab.hide()
showRemainingSocialAccountsView()
scrollToBottom()
})
}
override fun hideUsernameOrEmailView() = text_username_or_email.setVisible(false)
override fun hidePasswordView() = text_password.setVisible(false)
override fun hideSignUpView() = text_new_to_rocket_chat.setVisible(false)
override fun hideOauthView() {
social_accounts_container.setVisible(false)
button_fab.setVisible(false)
}
override fun hideLoginButton() = button_log_in.setVisible(false)
override fun alertWrongUsernameOrEmail() { override fun alertWrongUsernameOrEmail() {
AnimationHelper.vibrateSmartPhone(appContext) AnimationHelper.vibrateSmartPhone(appContext)
...@@ -164,24 +179,25 @@ class LoginFragment : Fragment(), LoginView { ...@@ -164,24 +179,25 @@ class LoginFragment : Fragment(), LoginView {
override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error)) override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error))
override fun showNoInternetConnection() =
override fun showNoInternetConnection() = showMessage(getString(R.string.msg_no_internet_connection)) showMessage(getString(R.string.msg_no_internet_connection))
private fun areLoginOptionsNeeded() { private fun areLoginOptionsNeeded() {
if (!isEditTextEmpty() || KeyboardHelper.isSoftKeyboardShown(scroll_view.rootView)) { if (!isEditTextEmpty() || KeyboardHelper.isSoftKeyboardShown(scroll_view.rootView)) {
showSignUpView(false) hideSignUpView()
showOauthView(false) hideOauthView()
showLoginButton(true) showLoginButton()
} else { } else {
showSignUpView(true) showSignUpView()
showOauthView(true) showOauthView()
showLoginButton(false) hideLoginButton()
} }
} }
private fun tintEditTextDrawableStart() { private fun tintEditTextDrawableStart() {
activity?.apply { activity?.apply {
val personDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_assignment_ind_black_24dp, this) val personDrawable =
DrawableHelper.getDrawableFromId(R.drawable.ic_assignment_ind_black_24dp, this)
val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_lock_black_24dp, this) val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_lock_black_24dp, this)
val drawables = arrayOf(personDrawable, lockDrawable) val drawables = arrayOf(personDrawable, lockDrawable)
...@@ -191,10 +207,6 @@ class LoginFragment : Fragment(), LoginView { ...@@ -191,10 +207,6 @@ class LoginFragment : Fragment(), LoginView {
} }
} }
private fun showLoginButton(value: Boolean) {
button_log_in.setVisible(value)
}
private fun setupSignUpListener() { private fun setupSignUpListener() {
val signUp = getString(R.string.title_sign_up) val signUp = getString(R.string.title_sign_up)
val newToRocketChat = String.format(getString(R.string.msg_new_user), signUp) val newToRocketChat = String.format(getString(R.string.msg_new_user), signUp)
...@@ -215,7 +227,8 @@ class LoginFragment : Fragment(), LoginView { ...@@ -215,7 +227,8 @@ class LoginFragment : Fragment(), LoginView {
} }
// Returns true if *all* EditTexts are empty. // Returns true if *all* EditTexts are empty.
private fun isEditTextEmpty(): Boolean = text_username_or_email.textContent.isBlank() && text_password.textContent.isEmpty() private fun isEditTextEmpty(): Boolean =
text_username_or_email.textContent.isBlank() && text_password.textContent.isEmpty()
private fun showRemainingSocialAccountsView() { private fun showRemainingSocialAccountsView() {
social_accounts_container.postDelayed({ social_accounts_container.postDelayed({
...@@ -242,4 +255,33 @@ class LoginFragment : Fragment(), LoginView { ...@@ -242,4 +255,33 @@ class LoginFragment : Fragment(), LoginView {
scroll_view.fullScroll(ScrollView.FOCUS_DOWN) scroll_view.fullScroll(ScrollView.FOCUS_DOWN)
}, 1250) }, 1250)
} }
}
@SuppressLint("SetJavaScriptEnabled")
private fun setupWebView(webPageUrl: String, requestedToken: String) {
web_view.settings.javaScriptEnabled = true
web_view.webViewClient = object : WebViewClient() {
override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
// The user can be already logged in the CAS, so check if the URL contains "ticket"
// (that means he/she is successful authenticated and we don't need to wait until the page is finished.
if (url.contains("ticket")) {
authenticateWithCas(requestedToken)
}
}
override fun onPageFinished(view: WebView, url: String) {
if (url.contains("ticket")) {
authenticateWithCas(requestedToken)
} else {
hideLoading()
web_view.setVisible(true)
}
}
}
web_view.loadUrl(webPageUrl)
}
private fun authenticateWithCas(requestedToken: String) {
web_view.setVisible(false)
presenter.authenticateWithCas(requestedToken)
}
}
\ No newline at end of file
...@@ -13,6 +13,17 @@ object UrlHelper { ...@@ -13,6 +13,17 @@ object UrlHelper {
*/ */
fun getAvatarUrl(serverUrl: String, avatarName: String): String = removeTrailingSlash(serverUrl) + "/avatar/" + avatarName fun getAvatarUrl(serverUrl: String, avatarName: String): String = removeTrailingSlash(serverUrl) + "/avatar/" + avatarName
/**
* Returns the CAS URL.
*
* @param casLoginUrl The CAS login URL from the server settings.
* @param serverUrl The server URL.
* @param token The token to be send to the CAS server.
* @return The avatar URL.
*/
fun getCasUrl(casLoginUrl: String, serverUrl: String, token: String): String =
removeTrailingSlash(casLoginUrl) + "?service=" + removeTrailingSlash(serverUrl) + "/_cas/" + token
/** /**
* Returns the server's Terms of Service URL. * Returns the server's Terms of Service URL.
* *
......
...@@ -14,10 +14,10 @@ class RefreshSettingsInteractor @Inject constructor(private val factory: RocketC ...@@ -14,10 +14,10 @@ class RefreshSettingsInteractor @Inject constructor(private val factory: RocketC
SITE_URL, SITE_NAME, FAVICON_512, USE_REALNAME, ALLOW_ROOM_NAME_SPECIAL_CHARS, SITE_URL, SITE_NAME, FAVICON_512, USE_REALNAME, ALLOW_ROOM_NAME_SPECIAL_CHARS,
FAVORITE_ROOMS, ACCOUNT_LOGIN_FORM, ACCOUNT_GOOGLE, ACCOUNT_FACEBOOK, ACCOUNT_GITHUB, FAVORITE_ROOMS, ACCOUNT_LOGIN_FORM, ACCOUNT_GOOGLE, ACCOUNT_FACEBOOK, ACCOUNT_GITHUB,
ACCOUNT_GITLAB, ACCOUNT_LINKEDIN, ACCOUNT_METEOR, ACCOUNT_TWITTER, ACCOUNT_WORDPRESS, ACCOUNT_GITLAB, ACCOUNT_LINKEDIN, ACCOUNT_METEOR, ACCOUNT_TWITTER, ACCOUNT_WORDPRESS,
LDAP_ENABLE, ACCOUNT_REGISTRATION, UPLOAD_STORAGE_TYPE, UPLOAD_MAX_FILE_SIZE, LDAP_ENABLE, CAS_ENABLE, CAS_LOGIN_URL, ACCOUNT_REGISTRATION, UPLOAD_STORAGE_TYPE,
UPLOAD_WHITELIST_MIMETYPES, HIDE_USER_JOIN, HIDE_USER_LEAVE, HIDE_TYPE_AU, HIDE_MUTE_UNMUTE, UPLOAD_MAX_FILE_SIZE, UPLOAD_WHITELIST_MIMETYPES, HIDE_USER_JOIN, HIDE_USER_LEAVE,
HIDE_TYPE_RU, ACCOUNT_CUSTOM_FIELDS, ALLOW_MESSAGE_DELETING, ALLOW_MESSAGE_EDITING, HIDE_TYPE_AU, HIDE_MUTE_UNMUTE, HIDE_TYPE_RU, ACCOUNT_CUSTOM_FIELDS, ALLOW_MESSAGE_DELETING,
ALLOW_MESSAGE_PINNING, SHOW_DELETED_STATUS, SHOW_EDITED_STATUS) ALLOW_MESSAGE_EDITING, ALLOW_MESSAGE_PINNING, SHOW_DELETED_STATUS, SHOW_EDITED_STATUS)
suspend fun refresh(server: String) { suspend fun refresh(server: String) {
withContext(CommonPool) { withContext(CommonPool) {
......
...@@ -29,6 +29,8 @@ const val USE_REALNAME = "UI_Use_Real_Name" ...@@ -29,6 +29,8 @@ const val USE_REALNAME = "UI_Use_Real_Name"
const val ALLOW_ROOM_NAME_SPECIAL_CHARS = "UI_Allow_room_names_with_special_chars" const val ALLOW_ROOM_NAME_SPECIAL_CHARS = "UI_Allow_room_names_with_special_chars"
const val FAVORITE_ROOMS = "Favorite_Rooms" const val FAVORITE_ROOMS = "Favorite_Rooms"
const val LDAP_ENABLE = "LDAP_Enable" const val LDAP_ENABLE = "LDAP_Enable"
const val CAS_ENABLE = "CAS_enabled"
const val CAS_LOGIN_URL = "CAS_login_url"
const val UPLOAD_STORAGE_TYPE = "FileUpload_Storage_Type" const val UPLOAD_STORAGE_TYPE = "FileUpload_Storage_Type"
const val UPLOAD_MAX_FILE_SIZE = "FileUpload_MaxFileSize" const val UPLOAD_MAX_FILE_SIZE = "FileUpload_MaxFileSize"
const val UPLOAD_WHITELIST_MIMETYPES = "FileUpload_MediaTypeWhiteList" const val UPLOAD_WHITELIST_MIMETYPES = "FileUpload_MediaTypeWhiteList"
...@@ -59,6 +61,8 @@ fun PublicSettings.wordpressEnabled(): Boolean = this[ACCOUNT_WORDPRESS]?.value ...@@ -59,6 +61,8 @@ fun PublicSettings.wordpressEnabled(): Boolean = this[ACCOUNT_WORDPRESS]?.value
fun PublicSettings.useRealName(): Boolean = this[USE_REALNAME]?.value == true fun PublicSettings.useRealName(): Boolean = this[USE_REALNAME]?.value == true
fun PublicSettings.ldapEnabled(): Boolean = this[LDAP_ENABLE]?.value == true fun PublicSettings.ldapEnabled(): Boolean = this[LDAP_ENABLE]?.value == true
fun PublicSettings.casEnabled(): Boolean = this[CAS_ENABLE]?.value == true
fun PublicSettings.casLoginUrl(): String = this[CAS_LOGIN_URL]?.value.toString()
// Message settings // Message settings
fun PublicSettings.showDeletedStatus(): Boolean = this[SHOW_DELETED_STATUS]?.value == true fun PublicSettings.showDeletedStatus(): Boolean = this[SHOW_DELETED_STATUS]?.value == true
......
...@@ -9,6 +9,7 @@ import android.widget.TextView ...@@ -9,6 +9,7 @@ import android.widget.TextView
import chat.rocket.android.widget.emoji.EmojiParser import chat.rocket.android.widget.emoji.EmojiParser
import chat.rocket.android.widget.emoji.EmojiTypefaceSpan import chat.rocket.android.widget.emoji.EmojiTypefaceSpan
import ru.noties.markwon.Markwon import ru.noties.markwon.Markwon
import java.security.SecureRandom
fun String.ifEmpty(value: String): String { fun String.ifEmpty(value: String): String {
if (isEmpty()) { if (isEmpty()) {
...@@ -34,6 +35,17 @@ fun EditText.erase() { ...@@ -34,6 +35,17 @@ fun EditText.erase() {
fun String.isEmailValid(): Boolean = Patterns.EMAIL_ADDRESS.matcher(this).matches() fun String.isEmailValid(): Boolean = Patterns.EMAIL_ADDRESS.matcher(this).matches()
fun generateRandomString(stringLength: Int): String {
val base = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
val secureRandom = SecureRandom()
val stringBuilder = StringBuilder(stringLength)
for (i in 0 until stringLength) {
stringBuilder.append(base[secureRandom.nextInt(base.length)])
}
return stringBuilder.toString()
}
var TextView.textContent: String var TextView.textContent: String
get() = text.toString() get() = text.toString()
set(value) { set(value) {
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
<android.support.constraint.ConstraintLayout <android.support.constraint.ConstraintLayout
android:id="@+id/middle_container" android:id="@+id/middle_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="match_parent">
<TextView <TextView
android:id="@+id/text_headline" android:id="@+id/text_headline"
...@@ -21,6 +21,21 @@ ...@@ -21,6 +21,21 @@
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<WebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="22dp"
android:layout_marginEnd="@dimen/screen_edge_left_and_right_margins"
android:layout_marginStart="@dimen/screen_edge_left_and_right_margins"
android:layout_marginTop="64dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_headline"
tools:visibility="visible" />
<EditText <EditText
android:id="@+id/text_username_or_email" android:id="@+id/text_username_or_email"
style="@style/Authentication.EditText" style="@style/Authentication.EditText"
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
<string name="title_sign_in_your_server">Faça login no seu servidor</string> <string name="title_sign_in_your_server">Faça login no seu servidor</string>
<string name="title_log_in">Entrar</string> <string name="title_log_in">Entrar</string>
<string name="title_sign_up">Inscreva-se</string> <string name="title_sign_up">Inscreva-se</string>
<string name="title_log_in_or_sign_up">Entrar ou Inscrever-se</string>
<string name="title_legal_terms">Termos Legais</string> <string name="title_legal_terms">Termos Legais</string>
<string name="title_chats">Chats</string> <string name="title_chats">Chats</string>
<string name="title_profile">Perfil</string> <string name="title_profile">Perfil</string>
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
<string name="title_sign_in_your_server">Sign in your server</string> <string name="title_sign_in_your_server">Sign in your server</string>
<string name="title_log_in">Log in</string> <string name="title_log_in">Log in</string>
<string name="title_sign_up">Sign up</string> <string name="title_sign_up">Sign up</string>
<string name="title_log_in_or_sign_up">Log in or Sign up</string>
<string name="title_legal_terms">Legal Terms</string> <string name="title_legal_terms">Legal Terms</string>
<string name="title_chats">Chats</string> <string name="title_chats">Chats</string>
<string name="title_profile">Profile</string> <string name="title_profile">Profile</string>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment