diff --git a/.rubocop.yml b/.rubocop.yml index a2f21d57c0..0d6f51b2a6 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -73,7 +73,7 @@ Metrics/ClassLength: # A calculated magnitude based on number of assignments, # branches, and conditions. Metrics/AbcSize: - Max: 85 + Max: 90 Metrics/ParameterLists: CountKeywordArgs: false @@ -82,10 +82,10 @@ RSpec/AnyInstance: Enabled: false Metrics/CyclomaticComplexity: - Max: 18 + Max: 20 Metrics/PerceivedComplexity: - Max: 18 + Max: 20 Rails/Exit: Exclude: diff --git a/app/assets/locales/el.json b/app/assets/locales/el.json index 6ea43f0b92..93cd8e9f7e 100644 --- a/app/assets/locales/el.json +++ b/app/assets/locales/el.json @@ -164,6 +164,10 @@ "wrong_access_code": "Λάθος κωδικός πρόσβασης", "generate_viewers_access_code": "Δημιουργία κωδικού πρόσβασης για θεατές", "generate_mods_access_code": "Δημιουργία κωδικού πρόσβασης για συντονιστές", + "server_tag": "Επιλέξτε ένα τύπο διακομιστή για αυτή την αίθουσα", + "default_tag_name": "Προεπιλογή", + "server_tag_desired": "Επιθυμητό", + "server_tag_required": "Απαιτείται", "are_you_sure_delete_room": "Θέλετε σίγουρα να διαγράψετε αυτή την αίθουσα;" } }, @@ -274,7 +278,7 @@ "administration": { "administration": "Διαχείριση", "terms": "Όροι και Προϋποθέσεις", - "privacy": "Πολιτική απορρήτου", + "privacy": "Πολιτική Απορρήτου", "privacy_policy": "Πολιτική Απορρήτου", "change_term_links": "Αλλαγή των συνδέσμων για τους όρους χρήσης που εμφανίζονται στο κάτω μέρος της σελίδας", "change_privacy_link": "Αλλαγή του συνδέσμου για το απόρρητο που εμφανίζεται στο κάτω μέρος της σελίδας", @@ -303,7 +307,7 @@ "registration": { "registration": "Εγγραφή", "role_mapping_by_email": "Εκχώρηση ρόλου με email", - "role_mapping_by_email_description": "Εκχώρηση ρόλου σε χρήστη με χρήση του email του. Πρέπει να είναι όπως: role1=email1, role2=email2", + "role_mapping_by_email_description": "Κατανείμετε το ρόλο χρήστη σύμφωνα με το email. Πρέπει να έχει τη μορφή role1=email1,role2=email2", "enter_role_mapping_rule": "Εισαγάγετε ένα κανόνα εκχώρησης ρόλου", "resync_on_login": "Συγχρονισμός δεδομένων χρήστη σε κάθε σύνδεση", "resync_on_login_description": "Επανασυγχρονισμός των πληροφοριών χρήστη σε κάθε σύνδεση, υποχρεώνοντας τον εξωτερικό πάροχο αυθεντικοποίησης σε συνεχή επιβεβαίωση των πληροφοριών στο Greenlight", @@ -315,7 +319,10 @@ "open": "Ενεργοποίηση εγγραφών", "invite": "Συμμετοχή με πρόσκληση", "approval": "Έγκριση/Απόρριψη" - } + }, + "allowed_domains": "Επιτρέπονται τα email ονομάτων τομεα", + "allowed_domains_signup_description": "Επιτρέπονται συγκεκριμένα email ονομάτων τομεα για εγγραφή. Η μορφή πρέπει να είναι @test.com,domain.com", + "enter_allowed_domains_rule" : "Προσθηκη επιτρεπόμενου ονόματος τομέα " } }, "room_configuration": { @@ -413,10 +420,11 @@ "brand_color_updated": "Το χρώμα επωνυμίας ενημερώθηκε.", "brand_image_updated": "Η εικόνα επωνυμίας ενημερώθηκε.", "brand_image_deleted": "Η εικόνα επωνυμίας διαγράφηκε.", - "privacy_policy_updated": "Η πολιτική ιδιωτικότητας ενημερώθηκε.", + "privacy_policy_updated": "Η πολιτική απορρήτου ενημερώθηκε.", "helpcenter_updated": "Ο σύνδεσμος για το Κέντρο βοήθειας ενημερώθηκε. ", "terms_of_service_updated": "Οι όριο χρήσης ενημερώθηκαν.", - "maintenance_updated": "Το μήνυμα της λειτουργίας συντήρησης ενημερώθηκε." + "maintenance_updated": "Το μήνυμα της λειτουργίας συντήρησης ενημερώθηκε.", + "allowed_domains_signup_updated": "Τα επιτρεπόμενα ονόματα τομέα ενημερώθηκαν" }, "recording": { "recording_visibility_updated": "Η εμφάνιση καταγραφής ενημερώθηκε.", @@ -437,6 +445,7 @@ }, "error": { "problem_completing_action": "Παρουσιάστηκε πρόβλημα σε αυτή την ενέργεια.\nΠαρακαλούμε δοκιμάστε ξανά.", + "server_type_unavailable": "Ο επιθυμητός τύπος διακομιστή δεν είναι διαθέσιμος. Παρακαλούμε επιλέξτε διαφορετικό τύπο από τις ρυθμίσεις αίθουσας. ", "file_type_not_supported": "Ο τύπος αρχείου δεν υποστηρίζεται.", "file_size_too_large": "Το μέγεθος αρχείου είναι πολύ μεγάλο.", "file_upload_error": "Το αρχείο δεν μπορεί να μεταφορτωθεί.", @@ -533,6 +542,11 @@ }, "url": { "invalid": "Μη έγκυρο URL" + }, + "text_form": { + "value": { + "required": "Παρακαλούμε πληκτρολογήστε το μήνυμά σας" + } } }, "room": { diff --git a/app/assets/locales/en.json b/app/assets/locales/en.json index 9c3e311ce7..d0f5068b2a 100644 --- a/app/assets/locales/en.json +++ b/app/assets/locales/en.json @@ -172,6 +172,10 @@ "wrong_access_code": "Wrong Access Code", "generate_viewers_access_code": "Generate access code for viewers", "generate_mods_access_code": "Generate access code for moderators", + "server_tag": "Select a server type for this room", + "default_tag_name": "Default", + "server_tag_desired": "Desired", + "server_tag_required": "Required", "are_you_sure_delete_room": "Are you sure you want to delete this room?" } }, @@ -282,8 +286,8 @@ "administration": { "administration": "Administration", "terms": "Terms & Conditions", - "privacy": "Privacy Policy", - "privacy_policy": "Privacy Policy", + "privacy": "Privacy Notice", + "privacy_policy": "Privacy Notice", "change_term_links": "Change the terms links that appears at the bottom of the page", "change_privacy_link": "Change the privacy link that appears at the bottom of the page", "helpcenter": "Help Center", @@ -311,7 +315,7 @@ "registration": { "registration": "Registration", "role_mapping_by_email": "Role Mapping By Email", - "role_mapping_by_email_description": "Map the user to a role using their email. Must be in the format: role1=email1, role2=email2", + "role_mapping_by_email_description": "Map the user to a role using their email. Must be in the format: role1=email1,role2=email2", "enter_role_mapping_rule": "Enter a role mapping rule", "resync_on_login": "Resync User Data On Every Sign In", "resync_on_login_description": "Resync a user's information every time they sign in, making the external authentication provider always match the information in Greenlight", @@ -323,7 +327,10 @@ "open": "Open Registration", "invite": "Join by Invitation", "approval": "Approve/Decline" - } + }, + "allowed_domains": "Allowed Email Domains", + "allowed_domains_signup_description": "Allow specific email domains to sign up. Format must be: @test.com,domain.com", + "enter_allowed_domains_rule" : "Enter the allowed domains" } }, "room_configuration": { @@ -423,10 +430,11 @@ "brand_color_updated": "The brand color has been updated.", "brand_image_updated": "The brand image has been updated.", "brand_image_deleted": "The brand image has been deleted.", - "privacy_policy_updated": "The privacy policy has been updated.", + "privacy_policy_updated": "The privacy notice has been updated.", "helpcenter_updated": "The help center link has been updated.", "terms_of_service_updated": "The terms of service have been updated.", - "maintenance_updated": "The maintenance banner has been updated." + "maintenance_updated": "The maintenance banner has been updated.", + "allowed_domains_signup_updated": "The allowed email domains have been updated." }, "recording": { "recording_visibility_updated": "The recording visibility has been updated.", @@ -447,6 +455,7 @@ }, "error": { "problem_completing_action": "The action can't be completed. \n Please try again.", + "server_type_unavailable": "The required server type is unavailable. Please select a different type in the room settings.", "file_type_not_supported": "The file type is not supported.", "file_size_too_large": "The file size is too large.", "file_upload_error": "The file can't be uploaded.", @@ -543,6 +552,11 @@ }, "url": { "invalid": "Invalid URL" + }, + "text_form": { + "value": { + "required": "Please enter some message" + } } }, "room": { diff --git a/app/assets/locales/fa_IR.json b/app/assets/locales/fa_IR.json index 1a1097fa7b..9aaa729a0c 100644 --- a/app/assets/locales/fa_IR.json +++ b/app/assets/locales/fa_IR.json @@ -164,6 +164,10 @@ "wrong_access_code": "کد دسترسی اشتباه است", "generate_viewers_access_code": "ایجاد کد دسترسی برای بینندگان", "generate_mods_access_code": "ایجاد کد دسترسی برای مدیران", + "server_tag": "نوع سرور برای این اتاق انتخاب کنید", + "default_tag_name": "پیش‌فرض", + "server_tag_desired": "دلخواه", + "server_tag_required": "الزامی است", "are_you_sure_delete_room": "آیا مطمئن هستید که می‌خواهید این اتاق را حذف کنید؟" } }, @@ -274,8 +278,8 @@ "administration": { "administration": "مدیریت", "terms": "شرایط و ضوابط", - "privacy": "سیاست حفظ حریم خصوصی", - "privacy_policy": "سیاست حفظ حریم خصوصی", + "privacy": "اطلاعیه حفظ حریم خصوصی", + "privacy_policy": "اطلاعیه حفظ حریم خصوصی", "change_term_links": "تغییر پیوندهای مقررات که در پایین صفحه نمایان می‌شوند", "change_privacy_link": "پیوند حریم خصوصی که در پایین صفحه نمایان می‌شود را تغییر دهید", "helpcenter": "مرکز راهنمایی", @@ -413,7 +417,7 @@ "brand_color_updated": "رنگ برند به‌روز شده‌است.", "brand_image_updated": "تصویر برند به‌روز شده‌است.", "brand_image_deleted": "تصویر برند حذف شده‌است.", - "privacy_policy_updated": "سیاست حفظ حریم خصوصی به‌روز شده‌است.", + "privacy_policy_updated": "اطلاعیه حفظ حریم خصوصی به‌روز شده است.", "helpcenter_updated": "پیوند مرکز راهنمایی به‌روز شده است.", "terms_of_service_updated": "شرایط خدمات به‌روز شده‌است.", "maintenance_updated": "اعلامیهٔ حالت تعمیر به‌روز شده است." @@ -437,6 +441,7 @@ }, "error": { "problem_completing_action": "این عمل نمی‌تواند تکمیل شود.\nلطفا دوباره تلاش کنید.", + "server_type_unavailable": "نوع سرور مورد نظر در دسترس نیست. لطفا نوع دیگری را در تنظیمات اتاق انتخاب کنید.", "file_type_not_supported": "نوع پرونده پشتیبانی نمی‌شود.", "file_size_too_large": "اندازهٔ پرونده خیلی بزرگ است.", "file_upload_error": "پرونده نمی‌تواند بارگذاری شود.", @@ -533,6 +538,11 @@ }, "url": { "invalid": "نشانی اینترنتی معتبر نیست" + }, + "text_form": { + "value": { + "required": "لطفا یک پیام وارد کنید" + } } }, "room": { diff --git a/app/assets/locales/gl.json b/app/assets/locales/gl.json index 49e2a9cbe9..a37ef4aed5 100644 --- a/app/assets/locales/gl.json +++ b/app/assets/locales/gl.json @@ -14,6 +14,7 @@ "report": "Informar", "share": "Compartir", "cancel": "Cancelar", + "reset": "Restabelecer", "close": "Pechar", "delete": "Eliminar", "copy": "Copiar a ligazón para unirse", @@ -163,6 +164,10 @@ "wrong_access_code": "Código de acceso incorrecto", "generate_viewers_access_code": "Xerar un código de acceso para os espectadores", "generate_mods_access_code": "Xerar un código de acceso para os moderadores", + "server_tag": "Seleccione un tipo de servidor para esta sala", + "default_tag_name": "Predeterminado", + "server_tag_desired": "Desexado", + "server_tag_required": "Obrigatorio", "are_you_sure_delete_room": "Confirma que quere eliminar esta sala?" } }, @@ -229,7 +234,8 @@ "empty_invited_users_subtext": "Cando o estado dun usuario cambia a convidado, aparece aquí.", "invited": { "time_sent": "Hora de envío", - "valid": "Valida" + "valid": "Valida", + "revoke": "Revogar" } }, "server_rooms": { @@ -272,8 +278,8 @@ "administration": { "administration": "Administración", "terms": "Terms & Conditions", - "privacy": "Directiva de privacidade", - "privacy_policy": "Directiva de privacidade", + "privacy": "Aviso de privacidade", + "privacy_policy": "Aviso de privacidade", "change_term_links": "Cambiar as ligazóns das condicións que aparecen na parte inferior da páxina", "change_privacy_link": "Cambiar a ligazón de privacidade que aparece na parte inferior da páxina", "helpcenter": "Centro de axuda", @@ -301,7 +307,7 @@ "registration": { "registration": "Rexistro", "role_mapping_by_email": "Asignación de roles por correo", - "role_mapping_by_email_description": "Asigne o usuario a un rol usando o seu correo electrónico. Debe ter o formato: rol1=correo-e1, rol2=correo-e2", + "role_mapping_by_email_description": "Asigne o usuario a un rol usando o seu correo-e. Debe ter o formato: rol1=correo-e1, rol2=correo-e2", "enter_role_mapping_rule": "Introduza unha regra de asignación de roles", "resync_on_login": "Volver sincronizar os datos do usuario en cada acceso", "resync_on_login_description": "Volver sincronizar a información dun usuario cada vez que acceda, facendo que o provedor de autenticación externo coincida sempre coa información en Greenlight", @@ -313,7 +319,10 @@ "open": "Rexistro aberto", "invite": "Unirse por convite", "approval": "Aceptar/declinar" - } + }, + "allowed_domains": "Dominios de correo-e permitidos", + "allowed_domains_signup_description": "Permitir o rexistro con dominios de correo-e específicos. O formato debe ser: @proba.com,dominio.com", + "enter_allowed_domains_rule" : "Introduza os dominios permitidos" } }, "room_configuration": { @@ -411,10 +420,11 @@ "brand_color_updated": "A cor corporativa foi actualizada.", "brand_image_updated": "A imaxe corporativa foi actualizada.", "brand_image_deleted": "A imaxe corporativa foi eliminada.", - "privacy_policy_updated": "A directiva de pivacidade foi actualizada.", + "privacy_policy_updated": "O aviso de pivacidade foi actualizado.", "helpcenter_updated": "Actualizouse a ligazón do centro de axuda.", "terms_of_service_updated": "Actualizáronse as condicións do servizo.", - "maintenance_updated": "Actualizouse o báner de mantemento." + "maintenance_updated": "Actualizouse o báner de mantemento.", + "allowed_domains_signup_updated": "Foron actualizados os dominios de correo-e permitidos." }, "recording": { "recording_visibility_updated": "Foi actualizada a visibilidade da gravación.", @@ -429,11 +439,13 @@ "role_permission_updated": "Foron actualizados os permisos de rol." }, "invitations": { - "invitation_sent": "Enviouse un convite" + "invitation_sent": "Enviouse un convite", + "invitation_revoked": "Foi revogado un convite" } }, "error": { "problem_completing_action": "Non é posíbel completar a acción.\n Tenteo de novo.", + "server_type_unavailable": "O tipo de servidor requirido non está dispoñíbel. Seleccione un tipo diferente nos axustes da sala.", "file_type_not_supported": "O tipo de ficheiro non é compatible.", "file_size_too_large": "O ficheiro é demasiado grande.", "file_upload_error": "Non é posíbel enviar o ficheiro.", @@ -530,6 +542,11 @@ }, "url": { "invalid": "URL incorrecto" + }, + "text_form": { + "value": { + "required": "Introduza algunha mensaxe" + } } }, "room": { diff --git a/app/assets/locales/hu.json b/app/assets/locales/hu.json index 9a5bec5926..225a0267de 100644 --- a/app/assets/locales/hu.json +++ b/app/assets/locales/hu.json @@ -14,6 +14,7 @@ "report": "Jelentés", "share": "Megosztás", "cancel": "Mégse", + "reset": "Reszetelés", "close": "Bezárás", "delete": "Törlés", "copy": "Másolás", @@ -163,6 +164,10 @@ "wrong_access_code": "Hibás hozzáférési kód", "generate_viewers_access_code": "Hozzáférési kód előállítása a látogatók számára", "generate_mods_access_code": "Hozzáférési kód előállítása a moderátorok számára", + "server_tag": "Válasszon szervertípust a szobához", + "default_tag_name": "Alapértelmezett", + "server_tag_desired": "Kívánt", + "server_tag_required": "Kötelező", "are_you_sure_delete_room": "Biztos, hogy törli ezt a szobát?" } }, @@ -229,7 +234,8 @@ "empty_invited_users_subtext": "Ha egy felhasználó állapota meghívottá válik, akkor itt fog megjelenni.", "invited": { "time_sent": "Küldési idő", - "valid": "Érvényes" + "valid": "Érvényes", + "revoke": "Visszavonás" } }, "server_rooms": { @@ -272,13 +278,17 @@ "administration": { "administration": "Üzemeltetés", "terms": "Felhasználási feltételek", - "privacy": "Adatvédelmi nyilatkozat", - "privacy_policy": "Adatvédelmi nyilatkozat", + "privacy": "Adatvédelmi közlemény", + "privacy_policy": "Adatvédelmi közlemény", "change_term_links": "A lap alján megjelenő felhasználási feltételek hivatkozásának módosítása", "change_privacy_link": "A lap alján megjelenő adatvédelmi hivatkozás módosítása", "helpcenter": "Súgóközpont", "change_helpcenter_link": "A profil legördülő alatt megjelenő Súgóközpont link módosítsa", + "maintenance": "Karbantartási szalaghirdetés", + "change_maintenance_text": "A fejlécben megjelenő karbantartási szalaghirdetés szövegének módosítása.", "change_url": "Webcím módosítása", + "set_text": "Szöveg beállítása", + "clear_banner": "Szalaghirdetés törlése", "enter_link": "Itt adja meg a hivatkozást" }, "settings": { @@ -288,7 +298,11 @@ "allow_users_to_preupload_presentation": "A felhasználók előre feltöltheti a bemutatójukat", "allow_users_to_preupload_presentation_description": "A felhasználók előre feltöltheti a használandó bemutatójukat az adott szoba alapértelmezett bemutatójaként", "default_visibility": "Felvétel alapértelmezett láthatósága", - "default_visibility_description": "Alapértelmezés szerint minden újonnan létrehozott felvételnél ez lesz a láthatóság" + "default_visibility_description": "Alapértelmezés szerint minden újonnan létrehozott felvételnél ez lesz a láthatóság", + "session_timeout": "Munkamenet időtúllépése", + "session_timeout_description": "A munkamenet időtúllépését az alapértelmezett 1 napos vagy a kiterjesztett 7 napos munkamenet sütire konfigurálhatja", + "default_session_timeout": "Alapértelmezett (1 nap)", + "extended_session_timeout": "Kiterjesztett (7 nap)" }, "registration": { "registration": "Regisztráció", @@ -305,7 +319,10 @@ "open": "Nyílt regisztráció", "invite": "Csatlakozás meghívással", "approval": "Engedélyezés/elutasítás" - } + }, + "allowed_domains": "Engedélyezett e-mail domainek", + "allowed_domains_signup_description": "Meghatározott e-mail domainek regisztrációjának engedélyezése. A helyes formátum: @test.hu, domain.hu", + "enter_allowed_domains_rule" : "Adja meg az engedélyezett domaineket" } }, "room_configuration": { @@ -403,9 +420,11 @@ "brand_color_updated": "A márkaszín sikerese módosította.", "brand_image_updated": "A márkaképet sikeresen módosította.", "brand_image_deleted": "A márkakép sikeresen törölte.", - "privacy_policy_updated": "Adatvédelmi nyilatkozat frissítve.", + "privacy_policy_updated": "Az adatvédelmi közleményt frissítették.", "helpcenter_updated": "A Súgóközpont linkjét sikeresen módosította.", - "terms_of_service_updated": "A szolgáltatási feltételek frissítésre kerültek." + "terms_of_service_updated": "A szolgáltatási feltételek frissítésre kerültek.", + "maintenance_updated": "A karbantartási szalaghirdetést frissítettük.", + "allowed_domains_signup_updated": "Az engedélyezett email domaineket sikeresen módosította." }, "recording": { "recording_visibility_updated": "Felvétel láthatósága frissítve.", @@ -420,11 +439,13 @@ "role_permission_updated": "Szerepkör jogosultsága frissítve" }, "invitations": { - "invitation_sent": "Meghívó elküldve" + "invitation_sent": "Meghívó elküldve", + "invitation_revoked": "A meghívást visszautasították" } }, "error": { "problem_completing_action": "Probléma lépett fel a művelet befejezése során. \n Próbálja újra.", + "server_type_unavailable": "A kiválasztott szervertípus nem érhető el. Kérem, válasszon másikat.", "file_type_not_supported": "A fájltípus nem támogatott.", "file_size_too_large": "A fájl túl nagy.", "file_upload_error": "A fájlt nem sikerült feltölteni.", @@ -521,6 +542,11 @@ }, "url": { "invalid": "Érvénytelen webcím" + }, + "text_form": { + "value": { + "required": "Írjon egy üzenetet" + } } }, "room": { @@ -676,7 +702,8 @@ "site_settings": { "fields": { "value": { - "placeholder": "Adja meg a hivatkozást itt…" + "placeholder": "Adja meg a hivatkozást itt…", + "text_placeholder": "Ide írjon..." } } }, diff --git a/app/assets/locales/ja.json b/app/assets/locales/ja.json index 62d3c7dbed..b54c5fb072 100644 --- a/app/assets/locales/ja.json +++ b/app/assets/locales/ja.json @@ -164,6 +164,10 @@ "wrong_access_code": "誤った接続コード", "generate_viewers_access_code": "視聴者用接続コードを作成する", "generate_mods_access_code": "司会者用接続コードを作成する", + "server_tag": "この会議室のサーバタイプを選択", + "default_tag_name": "通常", + "server_tag_desired": "最適", + "server_tag_required": "最小", "are_you_sure_delete_room": "本当にこの会議室を削除しますか?" } }, @@ -274,8 +278,8 @@ "administration": { "administration": "法的根拠", "terms": "利用規約", - "privacy": "プライバシーポリシー", - "privacy_policy": "プライバシーポリシー", + "privacy": "プライバシーに関する告知", + "privacy_policy": "プライバシーに関する告知", "change_term_links": "ページ下方の利用規約へのリンクを変更する", "change_privacy_link": "ページ下方のプライバシーポリシーへのリンクを変更する", "helpcenter": "ヘルプセンター", @@ -308,14 +312,17 @@ "resync_on_login": "サインインごとにユーザーのデーターを再同期", "resync_on_login_description": "サインインのたびにユーザーの情報を再同期し、外部の承認提供者の情報とGreenlightの情報とを常に一致させます", "default_role": "標準の役割", - "default_role_description": "新しく作成されたユーザーに自動的に割り当てられる役割です", + "default_role_description": "新しく作成されたユーザーにはこの役割が自動的に割り当てられます", "registration_method": "登録方法", "registration_method_description": "ユーザーがこのサイトに登録する方法を変更します", "registration_methods" : { "open": "誰でも自由に登録", "invite": "招待制", "approval": "承認制" - } + }, + "allowed_domains": "許可するEmailのドメイン", + "allowed_domains_signup_description": "特定のEmailドメインのみからのサインアップを許可します。@test.com や domain.com のような形式で記入してください。", + "enter_allowed_domains_rule" : "許可するドメインを入力" } }, "room_configuration": { @@ -413,10 +420,11 @@ "brand_color_updated": "サイトの基調色がアップデートされました。", "brand_image_updated": "ブランド画像がアップデートされました。", "brand_image_deleted": "ブランド画像が消去されました。", - "privacy_policy_updated": "プライバシーポリシーがアップデートされました。", + "privacy_policy_updated": "プライバシーに関する告知がアップデートされました。", "helpcenter_updated": "ヘルプセンターのリンクが更新されました。", "terms_of_service_updated": "利用規約が更新されました。", - "maintenance_updated": "メンテナンスバナーが更新されました。" + "maintenance_updated": "メンテナンスバナーが更新されました。", + "allowed_domains_signup_updated": "許可するEmailのドメインが更新されました。" }, "recording": { "recording_visibility_updated": "録画の公開度が更新されました。", @@ -437,6 +445,7 @@ }, "error": { "problem_completing_action": "操作をうまく完了できませんでした。\nもう一度やってみてください。", + "server_type_unavailable": "希望されたサーバタイプは利用できません。別のタイプを部屋の設定から選んでください。", "file_type_not_supported": "ファイルの形式がサポートされていません。", "file_size_too_large": "ファイルが大きすぎます。", "file_upload_error": "ファイルがアップロードできません。", @@ -533,6 +542,11 @@ }, "url": { "invalid": "無効なURL" + }, + "text_form": { + "value": { + "required": "何かメッセージを入力してください" + } } }, "room": { diff --git a/app/assets/locales/ru.json b/app/assets/locales/ru.json index a5da2be4a9..dcb62ff136 100644 --- a/app/assets/locales/ru.json +++ b/app/assets/locales/ru.json @@ -14,9 +14,12 @@ "report": "Отчет", "share": "Предоставить доступ", "cancel": "Отмена", + "reset": "Сбросить", "close": "Закрыть", "delete": "Удалить", "copy": "Скопировать ссылку", + "copy_viewer_code": "Скопировать код участника", + "copy_moderator_code": "Скопировать код модератора", "or": "Или", "online": "Онлайн", "help_center": "Центр поддержки", @@ -161,6 +164,10 @@ "wrong_access_code": "Неверный код доступа", "generate_viewers_access_code": "Код доступа для участников", "generate_mods_access_code": "Код доступа для модераторов", + "server_tag": "Выберите тип сервера для этой комнаты", + "default_tag_name": "По умолчанию", + "server_tag_desired": "Желаемый", + "server_tag_required": "Обязательный", "are_you_sure_delete_room": "Вы уверены, что хотите удалить эту комнату?" } }, @@ -227,7 +234,8 @@ "empty_invited_users_subtext": "Когда статус пользователя изменится на «приглашенный», он появится здесь.", "invited": { "time_sent": "Время отправки", - "valid": "Действителен" + "valid": "Действителен", + "revoke": "Отозвать" } }, "server_rooms": { @@ -276,7 +284,11 @@ "change_privacy_link": "Изменить ссылку на политику конфиденциальности, которая появляется внизу страницы", "helpcenter": "Справочный центр", "change_helpcenter_link": "Измените ссылку Справочного центра, которая появляется под раскрывающимся списком профиля", + "maintenance": "Баннер технического обслуживания", + "change_maintenance_text": "Измените текст баннера технического обслуживания, который отображается в заголовке", "change_url": "Изменить URL", + "set_text": "Задать текст", + "clear_banner": "Очистить баннер", "enter_link": "Введите ссылку здесь" }, "settings": { @@ -286,7 +298,11 @@ "allow_users_to_preupload_presentation": "Разрешить пользователям предварительно загружать презентации", "allow_users_to_preupload_presentation_description": "Пользователи могут предварительно загрузить презентацию, которая будет использоваться в качестве презентации по умолчанию для этой комнаты", "default_visibility": "Статус видимости записи по умолчанию", - "default_visibility_description": "Все вновь созданные записи будут иметь этот статус видимости по умолчанию." + "default_visibility_description": "Все вновь созданные записи будут иметь этот статус видимости по умолчанию.", + "session_timeout": "Время ожидания сеанса", + "session_timeout_description": "Вы можете настроить время ожидания сеанса с помощью стандартного куки на 1 день или расширенного куки сеанса на 7 дней", + "default_session_timeout": "Стандартный (1 день)", + "extended_session_timeout": "Расширенный (7 дней)" }, "registration": { "registration": "Регистрация", @@ -303,7 +319,10 @@ "open": "Открытая регистрация", "invite": "Вход по приглашению", "approval": "Одобрить/отклонить" - } + }, + "allowed_domains": "Разрешенные домены электронной почты", + "allowed_domains_signup_description": "Разрешить определенные домены электронной почты для регистрации. Формат должен быть: @test.com,domain.com", + "enter_allowed_domains_rule" : "Введите разрешенные домены" } }, "room_configuration": { @@ -392,7 +411,9 @@ "access_code_copied": "Код доступа скопирован.", "access_code_generated": "Код доступа создан.", "access_code_deleted": "Код доступа удален.", - "copied_meeting_url": "Ссылка встречи скопирована. Ссылку можно использовать для присоединения к встрече." + "copied_meeting_url": "Ссылка встречи скопирована. Ссылку можно использовать для присоединения к встрече.", + "copied_viewer_code": "Код доступа участника вебинара был скопирован.", + "copied_moderator_code": "Код доступа модератора вебинара был скопирован." }, "site_settings": { "site_setting_updated": "Настройки сайта обновлены.", @@ -401,7 +422,9 @@ "brand_image_deleted": "Фирменное изображение удалено.", "privacy_policy_updated": "Политика конфиденциальности обновлена.", "helpcenter_updated": "Ссылка на Справочный центр обновлена", - "terms_of_service_updated": "Условия использования обновлены." + "terms_of_service_updated": "Условия использования обновлены.", + "maintenance_updated": "Баннер технического обслуживания обновлен.", + "allowed_domains_signup_updated": "Разрешенные домены электронной почты были обновлены" }, "recording": { "recording_visibility_updated": "Видимость записи обновлена.", @@ -416,15 +439,18 @@ "role_permission_updated": "Права роли обновлены." }, "invitations": { - "invitation_sent": "Выслать приглашение" + "invitation_sent": "Выслать приглашение", + "invitation_revoked": "Приглашение было отозвано" } }, "error": { "problem_completing_action": "Не удалось завершить данное действие. \n Пожалуйста, попробуйте еще раз.", + "server_type_unavailable": "Требуемый тип сервера недоступен. Пожалуйста, выберите другой тип в настройках комнаты", "file_type_not_supported": "Тип файла не поддерживается", "file_size_too_large": "Размер файла слишком большой", "file_upload_error": "Файл не может быть загружен.", "signin_required": "Для доступа к странице вы должны авторизоваться в системе. ", + "malware_detected": "Обнаружена вредоносная программа! Файл, который вы загрузили, может содержать вредоносное ПО. Пожалуйста, проверьте ваш файл и попробуйте снова.", "roles": { "role_assigned": "Эту роль нельзя удалить, т.к. она назначена как минимум одному пользователю." }, @@ -516,6 +542,11 @@ }, "url": { "invalid": "Неверная ссылка" + }, + "text_form": { + "value": { + "required": "Пожалуйста, введите сообщение" + } } }, "room": { @@ -671,7 +702,8 @@ "site_settings": { "fields": { "value": { - "placeholder": "Введите ссылку сюда..." + "placeholder": "Введите ссылку сюда...", + "text_placeholder": "Введите текст здесь..." } } }, diff --git a/app/assets/locales/tr.json b/app/assets/locales/tr.json index c6188d597a..1b4889e472 100644 --- a/app/assets/locales/tr.json +++ b/app/assets/locales/tr.json @@ -14,6 +14,7 @@ "report": "Bildir", "share": "Paylaş", "cancel": "İptal", + "reset": "Sıfırla", "close": "Kapat", "delete": "Sil", "copy": "Kopyala", @@ -163,6 +164,10 @@ "wrong_access_code": "Erişim kodu yanlış", "generate_viewers_access_code": "İzleyiciler için erişim kodu oluştur", "generate_mods_access_code": "Sorumlular için erişim kodu oluştur", + "server_tag": "Bu oda için bir sunucu türü seçin", + "default_tag_name": "Varsayılan", + "server_tag_desired": "İstenilen", + "server_tag_required": "Zorunlu", "are_you_sure_delete_room": "Bu odayı silmek istediğinize emin misiniz?" } }, @@ -229,7 +234,8 @@ "empty_invited_users_subtext": "Durumu çağrılmış olarak değiştirilen kullanıcılar burada görüntülenir.", "invited": { "time_sent": "Gönderilme zamanı", - "valid": "Geçerli" + "valid": "Geçerli", + "revoke": "Geçersiz kıl" } }, "server_rooms": { @@ -272,8 +278,8 @@ "administration": { "administration": "Yönetim", "terms": "Hüküm ve koşullar", - "privacy": "Gizlilik ilkesi", - "privacy_policy": "Gizlilik ilkesi", + "privacy": "Gizlilik bildirimi", + "privacy_policy": "Gizlilik bildirimi", "change_term_links": "Sayfanın altında görüntülenecek koşullar bağlantısını değiştir", "change_privacy_link": "Sayfanın altında görüntülenecek gizlilik bağlantısını değiştir", "helpcenter": "Yardım merkezi", @@ -301,7 +307,7 @@ "registration": { "registration": "Hesap açma", "role_mapping_by_email": "Roller e-postaya göre eşleştirilsin", - "role_mapping_by_email_description": "Kullanıcı rollerini e-postalarına göre belirler. rol1?e-posta1, rol2=e-posta2 biçiminde olmalıdır", + "role_mapping_by_email_description": "Kullanıcı rollerini e-postalarına göre belirler. rol1?e-posta1,rol2=e-posta2 biçiminde olmalıdır", "enter_role_mapping_rule": "Bir rol eşleştirme kuralı yazın", "resync_on_login": "Kullanıcı verileri her oturum açıldığında eşitlensin", "resync_on_login_description": "Bir kullanıcının bilgilerini her oturum açtığından yeniden eşitleyerek, Greenlight üzerindeki bilgilerin her zaman dış kimlik doğrulama hizmeti sağlayıcısındakiler ile aynı olmasını sağlar", @@ -313,7 +319,10 @@ "open": "Serbest hesap açma", "invite": "Çağrı ile katılma", "approval": "Onayla/Reddet" - } + }, + "allowed_domains": "İzin verilen e-posta etki alanları", + "allowed_domains_signup_description": "Belirli e-posta etki alanlarından hesap açılmasına izin verir. Şu biçimde yazılmalıdır: @deneme.com,etkialani.com", + "enter_allowed_domains_rule" : "İzin verilen etki alanlarını yazın" } }, "room_configuration": { @@ -411,10 +420,11 @@ "brand_color_updated": "Marka rengi güncellendi.", "brand_image_updated": "Marka görseli güncellendi.", "brand_image_deleted": "Marka görseli silindi.", - "privacy_policy_updated": "Gizlilik ilkesi güncellendi.", + "privacy_policy_updated": "Gizlilik bildirimi güncellendi.", "helpcenter_updated": "Yardım merkezi bağlantısı güncellendi.", "terms_of_service_updated": "Hizmet koşulları güncellendi.", - "maintenance_updated": "Bakım duyurusu güncellendi." + "maintenance_updated": "Bakım duyurusu güncellendi.", + "allowed_domains_signup_updated": "İzin verilen e-posta etki alanları güncellendi." }, "recording": { "recording_visibility_updated": "Kayıt görünürlüğü güncellendi.", @@ -429,11 +439,13 @@ "role_permission_updated": "Rol yetkileri güncellendi." }, "invitations": { - "invitation_sent": "Çağrı gönderildi" + "invitation_sent": "Çağrı gönderildi", + "invitation_revoked": "Bir çağrı geçersiz kılındı" } }, "error": { "problem_completing_action": "Bu işlem yapılırken bir sorun çıktı.\nLütfen yeniden deneyin.", + "server_type_unavailable": "İstenilen sunucu türü kullanılamıyor. Lütfen oda ayarlarında başka bir tür seçin.", "file_type_not_supported": "Dosya türü desteklenmiyor.", "file_size_too_large": "Dosya boyutu çok büyük.", "file_upload_error": "Dosya yüklenemedi.", @@ -530,6 +542,11 @@ }, "url": { "invalid": "Adres geçersiz" + }, + "text_form": { + "value": { + "required": "Lütfen bir ileti yazın" + } } }, "room": { diff --git a/app/controllers/api/v1/locales_controller.rb b/app/controllers/api/v1/locales_controller.rb index 911f2d73af..f86af3b2eb 100644 --- a/app/controllers/api/v1/locales_controller.rb +++ b/app/controllers/api/v1/locales_controller.rb @@ -48,11 +48,13 @@ def index # Returns the requested language's locale strings (returns 406 if locale doesn't exist) def show language = params[:name].tr('-', '_') + language_file = Dir.entries('app/assets/locales').select { |f| f.starts_with?(language) } + final_language = language_file.min&.gsub('.json', '') # Serve locales files directly in development (not through asset pipeline) - return render file: Rails.root.join('app', 'assets', 'locales', "#{language}.json") if Rails.env.development? + return render file: Rails.root.join('app', 'assets', 'locales', "#{final_language}.json") if Rails.env.development? - redirect_to ActionController::Base.helpers.asset_path("#{language}.json") + redirect_to ActionController::Base.helpers.asset_path("#{final_language}.json") rescue StandardError head :not_acceptable end diff --git a/app/controllers/api/v1/meetings_controller.rb b/app/controllers/api/v1/meetings_controller.rb index be51924d4b..a0dddd6657 100644 --- a/app/controllers/api/v1/meetings_controller.rb +++ b/app/controllers/api/v1/meetings_controller.rb @@ -31,12 +31,13 @@ def start begin MeetingStarter.new(room: @room, base_url: request.base_url, current_user:, provider: current_provider).call rescue BigBlueButton::BigBlueButtonException => e - return render_error status: :bad_request unless e.key == 'idNotUnique' + return render_error status: :bad_request, errors: e.key unless e.key == 'idNotUnique' end render_data data: BigBlueButtonApi.new(provider: current_provider).join_meeting( room: @room, name: current_user.name, + user_id: fetch_bbb_user_id, avatar_url: current_user.avatar.attached? ? url_for(current_user.avatar) : nil, role: 'Moderator' ), status: :created @@ -81,6 +82,7 @@ def status data[:joinUrl] = BigBlueButtonApi.new(provider: current_provider).join_meeting( room: @room, name: current_user ? current_user.name : params[:name], + user_id: fetch_bbb_user_id, avatar_url: current_user&.avatar&.attached? ? url_for(current_user.avatar) : nil, role: bbb_role ) @@ -145,6 +147,21 @@ def infer_bbb_role(mod_code:, viewer_code:, anyone_join_as_mod:) 'Viewer' end end + + def fetch_bbb_user_id + return "gl-#{current_user.id}" if current_user + + return cookies[:guest_id] if cookies[:guest_id].present? + + guest_id = "gl-guest-#{SecureRandom.hex(12)}" + + cookies[:guest_id] = { + value: guest_id, + expires: 1.day.from_now + } + + guest_id + end end end end diff --git a/app/controllers/api/v1/server_tags_controller.rb b/app/controllers/api/v1/server_tags_controller.rb new file mode 100644 index 0000000000..8692a657f4 --- /dev/null +++ b/app/controllers/api/v1/server_tags_controller.rb @@ -0,0 +1,37 @@ +# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/. +# +# Copyright (c) 2022 BigBlueButton Inc. and by respective authors (see below). +# +# This program is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free Software +# Foundation; either version 3.0 of the License, or (at your option) any later +# version. +# +# Greenlight is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with Greenlight; if not, see . + +# frozen_string_literal: true + +module Api + module V1 + class ServerTagsController < ApiController + # GET /api/v1/server_tags/:friendly_id + # Returns a list of all allowed tags&names for the room's owner + def show + tag_names = Rails.configuration.server_tag_names + tag_roles = Rails.configuration.server_tag_roles + return render_data data: {}, status: :ok if tag_names.blank? + + room = Room.find_by(friendly_id: params[:friendly_id]) + return render_data data: {}, status: :ok if room.nil? + + allowed_tag_names = tag_names.reject { |tag, _| tag_roles.key?(tag) && tag_roles[tag].exclude?(room.user.role_id) } + render_data data: allowed_tag_names, status: :ok + end + end + end +end diff --git a/app/controllers/api/v1/users_controller.rb b/app/controllers/api/v1/users_controller.rb index d28c0834a3..6661d71a91 100644 --- a/app/controllers/api/v1/users_controller.rb +++ b/app/controllers/api/v1/users_controller.rb @@ -61,6 +61,9 @@ def create # Users created by a user will have the creator language by default with a fallback to the server configured default_locale. create_user_params[:language] = current_user&.language || I18n.default_locale if create_user_params[:language].blank? + # renders an error if the user is signing up with an invalid domain based off site settings + return render_error errors: Rails.configuration.custom_error_msgs[:unauthorized], status: :forbidden unless valid_domain? + user = UserCreator.new(user_params: create_user_params.except(:invite_token), provider: current_provider, role: default_role).call smtp_enabled = ENV['SMTP_SERVER'].present? @@ -184,6 +187,17 @@ def valid_invite_token Invitation.destroy_by(email: create_user_params[:email].downcase, provider: current_provider, token: create_user_params[:invite_token]).present? end + + def valid_domain? + allowed_domains_emails = SettingGetter.new(setting_name: 'AllowedDomains', provider: current_provider).call + return true if allowed_domains_emails.blank? + + domains = allowed_domains_emails.split(',') + domains.each do |domain| + return true if create_user_params[:email].end_with?(domain) + end + false + end end end end diff --git a/app/controllers/external_controller.rb b/app/controllers/external_controller.rb index 085805aa19..702fd8633a 100644 --- a/app/controllers/external_controller.rb +++ b/app/controllers/external_controller.rb @@ -32,6 +32,13 @@ def create_user user = User.find_by(external_id: credentials['uid'], provider:) + # Fallback mechanism to search by email + if user.blank? && ENV.fetch('USE_EMAIL_AS_EXTERNAL_ID_FALLBACK', 'false') == 'true' + user = User.find_by(email: credentials['info']['email'], provider:) + # Update the user's external id to the latest value to avoid using the fallback + user.update(external_id: credentials['uid']) if user.present? && credentials['uid'].present? + end + new_user = user.blank? registration_method = SettingGetter.new(setting_name: 'RegistrationMethod', provider: current_provider).call @@ -41,6 +48,8 @@ def create_user return redirect_to root_path(error: Rails.configuration.custom_error_msgs[:invite_token_invalid]) end + return render_error status: :forbidden unless valid_domain?(user_info[:email]) + # Create the user if they dont exist if new_user user = UserCreator.new(user_params: user_info, provider: current_provider, role: default_role).call @@ -164,4 +173,15 @@ def build_user_info(credentials) verified: true } end + + def valid_domain?(email) + allowed_domain_emails = SettingGetter.new(setting_name: 'AllowedDomains', provider: current_provider).call + return true if allowed_domain_emails.blank? + + domains = allowed_domain_emails.split(',') + domains.each do |domain| + return true if email.end_with?(domain) + end + false + end end diff --git a/app/javascript/components/admin/site_settings/administration/LinksForm.jsx b/app/javascript/components/admin/site_settings/administration/LinksForm.jsx index b92aa3680c..ce500ede70 100644 --- a/app/javascript/components/admin/site_settings/administration/LinksForm.jsx +++ b/app/javascript/components/admin/site_settings/administration/LinksForm.jsx @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License along // with Greenlight; if not, see . -import React, { useEffect } from 'react'; +import React, { useCallback, useEffect, useRef } from 'react'; import PropTypes from 'prop-types'; import { Button } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; @@ -29,13 +29,28 @@ export default function LinksForm({ id, value, mutation: useUpdateSiteSettingsAP const { methods, fields } = useLinksForm({ defaultValues: { value } }); + const formText = useRef(''); + useEffect(() => { - if (!methods) { return; } - methods.reset({ value }); + if (methods) { + methods.reset({ value }); + formText.current = value; + } }, [methods, value]); + const handleSubmit = useCallback( + (formData) => { + if (formText.current !== formData[`${fields.value.hookForm.id}`]) { + formText.current = formData[`${fields.value.hookForm.id}`]; + return updateSiteSettingsAPI.mutate(formData); + } + return null; + }, + [updateSiteSettingsAPI.mutate], + ); + return ( -
+ . -import React, { useEffect } from 'react'; +import React, { useCallback, useEffect, useRef } from 'react'; import PropTypes from 'prop-types'; import { Button } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; @@ -25,39 +25,80 @@ import FormControl from '../../../shared_components/forms/FormControl'; import useTextForm from '../../../../hooks/forms/admin/site_settings/useTextForm'; export default function TextForm({ id, value, mutation: useUpdateSiteSettingsAPI }) { - const updateSiteSettingsAPI = useUpdateSiteSettingsAPI(); + const updateSiteSettingsAPISetText = useUpdateSiteSettingsAPI(); + const updateSiteSettingsAPIClearText = useUpdateSiteSettingsAPI(); + const { t } = useTranslation(); - const maintenanceBannerId = localStorage.getItem('maintenanceBannerId'); const { methods, fields } = useTextForm({ defaultValues: { value } }); + const formText = useRef(''); + useEffect(() => { - if (!methods) { return; } - methods.reset({ value }); + if (methods) { + methods.reset({ value }); + formText.current = value; + } }, [methods, value]); + const dismissMaintenanceBannerToast = () => { + const maintenanceBannerId = localStorage.getItem('maintenanceBannerId'); + if (maintenanceBannerId) { + toast.dismiss(maintenanceBannerId); + localStorage.removeItem('maintenanceBannerId'); + } + }; + // Function to clear the form const clearForm = () => { methods.reset({ value: '' }); - toast.dismiss(maintenanceBannerId); - updateSiteSettingsAPI.mutate(''); + dismissMaintenanceBannerToast(); + if (formText.current) { + formText.current = ''; + updateSiteSettingsAPIClearText.mutate(''); + } }; + const handleSubmit = useCallback((formData) => { + if (formText.current !== formData[`${fields.value.hookForm.id}`]) { + dismissMaintenanceBannerToast(); + formText.current = formData[`${fields.value.hookForm.id}`]; + return updateSiteSettingsAPISetText.mutate(formData); + } + return null; + }, [updateSiteSettingsAPISetText.mutate]); + return ( - + - - ); diff --git a/app/javascript/components/admin/site_settings/registration/Registration.jsx b/app/javascript/components/admin/site_settings/registration/Registration.jsx index 6b75f888bc..7d9029666f 100644 --- a/app/javascript/components/admin/site_settings/registration/Registration.jsx +++ b/app/javascript/components/admin/site_settings/registration/Registration.jsx @@ -28,11 +28,12 @@ import useRoles from '../../../../hooks/queries/admin/roles/useRoles'; export default function Registration() { const { t } = useTranslation(); const { data: env } = useEnv(); - const { data: siteSettings } = useSiteSettings(['RoleMapping', 'DefaultRole', 'ResyncOnLogin', 'RegistrationMethod']); + const { data: siteSettings } = useSiteSettings(['RoleMapping', 'DefaultRole', 'ResyncOnLogin', 'RegistrationMethod', 'AllowedDomains']); const { data: roles } = useRoles(); const updateRegistrationMethod = useUpdateSiteSetting('RegistrationMethod'); const updateDefaultRole = useUpdateSiteSetting('DefaultRole'); const updateRoleMapping = useUpdateSiteSetting('RoleMapping'); + const updateDomainSignUp = useUpdateSiteSetting('AllowedDomains'); return ( <> @@ -99,6 +100,24 @@ export default function Registration() { + + + {t('admin.site_settings.registration.allowed_domains')} +

{t('admin.site_settings.registration.allowed_domains_signup_description')}

+ + + + +
); } diff --git a/app/javascript/components/rooms/room/room_settings/RoomSettings.jsx b/app/javascript/components/rooms/room/room_settings/RoomSettings.jsx index 159f8ed303..973ca6afc0 100644 --- a/app/javascript/components/rooms/room/room_settings/RoomSettings.jsx +++ b/app/javascript/components/rooms/room/room_settings/RoomSettings.jsx @@ -33,6 +33,8 @@ import { useAuth } from '../../../../contexts/auth/AuthProvider'; import UpdateRoomNameForm from './forms/UpdateRoomNameForm'; import useRoom from '../../../../hooks/queries/rooms/useRoom'; import UnshareRoom from './UnshareRoom'; +import useServerTags from '../../../../hooks/queries/rooms/useServerTags'; +import ServerTagRow from './ServerTagRow'; export default function RoomSettings() { const { t } = useTranslation(); @@ -41,6 +43,7 @@ export default function RoomSettings() { const roomSetting = useRoomSettings(friendlyId); const { data: roomConfigs } = useRoomConfigs(); const { data: room } = useRoom(friendlyId); + const { data: serverTags } = useServerTags(friendlyId); const updateMutationWrapper = () => useUpdateRoomSetting(friendlyId); const deleteMutationWrapper = (args) => useDeleteRoom({ friendlyId, ...args }); @@ -66,6 +69,15 @@ export default function RoomSettings() { config={roomConfigs?.glModeratorAccessCode} description={t('room.settings.generate_mods_access_code')} /> + {serverTags && Object.keys(serverTags).length !== 0 && ( + + )}
{ t('room.settings.user_settings') }
diff --git a/app/javascript/components/rooms/room/room_settings/ServerTagRow.jsx b/app/javascript/components/rooms/room/room_settings/ServerTagRow.jsx new file mode 100644 index 0000000000..044df49710 --- /dev/null +++ b/app/javascript/components/rooms/room/room_settings/ServerTagRow.jsx @@ -0,0 +1,118 @@ +// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/. +// +// Copyright (c) 2022 BigBlueButton Inc. and by respective authors (see below). +// +// This program is free software; you can redistribute it and/or modify it under the +// terms of the GNU Lesser General Public License as published by the Free Software +// Foundation; either version 3.0 of the License, or (at your option) any later +// version. +// +// Greenlight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along +// with Greenlight; if not, see . + +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import PropTypes from 'prop-types'; +import { + Row, Col, Dropdown, ButtonGroup, ToggleButton, +} from 'react-bootstrap'; +import SimpleSelect from '../../../shared_components/utilities/SimpleSelect'; + +export default function ServerTagRow({ + updateMutation: useUpdateAPI, currentTag, tagRequired, serverTags, description, +}) { + const updateAPI = useUpdateAPI(); + const { t } = useTranslation(); + + function getDefaultTagName() { + return t('room.settings.default_tag_name'); + } + + function getTagName(tag) { + if (tag in serverTags) { + return serverTags[tag]; + } + return getDefaultTagName(); + } + + const dropdownTags = Object.entries(serverTags).map(([tagString, tagName]) => ( + ( + updateAPI.mutate({ settingName: 'serverTag', settingValue: tagString })} + > + {tagName} + + ) + )); + + return ( + +
{description}
+ + + {[ + updateAPI.mutate({ settingName: 'serverTag', settingValue: '' })} + > + {getDefaultTagName()} + , + ].concat(dropdownTags)} + + + + + { + updateAPI.mutate({ settingName: 'serverTagRequired', settingValue: false }); + }} + > + {t('room.settings.server_tag_desired')} + + { + updateAPI.mutate({ settingName: 'serverTagRequired', settingValue: true }); + }} + > + {t('room.settings.server_tag_required')} + + + +
+ ); +} + +ServerTagRow.defaultProps = { + currentTag: '', + tagRequired: false, +}; + +ServerTagRow.propTypes = { + updateMutation: PropTypes.func.isRequired, + currentTag: PropTypes.string, + tagRequired: PropTypes.bool, + serverTags: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types + description: PropTypes.string.isRequired, +}; diff --git a/app/javascript/components/shared_components/Footer.jsx b/app/javascript/components/shared_components/Footer.jsx index 30a3b5b2b4..2b30506e3c 100644 --- a/app/javascript/components/shared_components/Footer.jsx +++ b/app/javascript/components/shared_components/Footer.jsx @@ -19,17 +19,20 @@ import { useTranslation } from 'react-i18next'; import { Container } from 'react-bootstrap'; import useEnv from '../../hooks/queries/env/useEnv'; import useSiteSetting from '../../hooks/queries/site_settings/useSiteSetting'; +import { useAuth } from '../../contexts/auth/AuthProvider'; export default function Footer() { const { t } = useTranslation(); const { data: env } = useEnv(); const { data: links } = useSiteSetting(['Terms', 'PrivacyPolicy']); + const currentUser = useAuth(); + const isAdmin = currentUser && currentUser.role && currentUser?.role.name === 'Administrator'; return (