Commit 2db9aae7 authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch 'jswain_joining_team' into 'master'

Allow users to bypass the registration flow

See merge request gitlab-org/gitlab!72827
parents b5f31606 007ca183
...@@ -18,7 +18,7 @@ module Registrations ...@@ -18,7 +18,7 @@ module Registrations
if result[:status] == :success if result[:status] == :success
return redirect_to issues_dashboard_path(assignee_username: current_user.username) if show_tasks_to_be_done? return redirect_to issues_dashboard_path(assignee_username: current_user.username) if show_tasks_to_be_done?
return redirect_to experiment(:combined_registration, user: current_user).redirect_path(trial_params) if show_signup_onboarding? return redirect_to update_success_path if show_signup_onboarding?
members = current_user.members members = current_user.members
...@@ -66,6 +66,7 @@ module Registrations ...@@ -66,6 +66,7 @@ module Registrations
members.last.source.activity_path members.last.source.activity_path
end end
# overridden in EE
def show_signup_onboarding? def show_signup_onboarding?
false false
end end
...@@ -76,8 +77,12 @@ module Registrations ...@@ -76,8 +77,12 @@ module Registrations
MemberTask.for_members(current_user.members).exists? MemberTask.for_members(current_user.members).exists?
end end
# overridden in EE
def trial_params def trial_params
nil end
# overridden in EE
def update_success_path
end end
end end
end end
......
...@@ -24,9 +24,10 @@ ...@@ -24,9 +24,10 @@
.form-group.col-sm-12.js-other-role-group.hidden .form-group.col-sm-12.js-other-role-group.hidden
= f.label :other_role, _('What is your job title? (optional)'), class: 'form-check-label gl-mb-3' = f.label :other_role, _('What is your job title? (optional)'), class: 'form-check-label gl-mb-3'
= f.text_field :other_role, class: 'form-control' = f.text_field :other_role, class: 'form-control'
= render_if_exists "registrations/welcome/jobs_to_be_done", f: f
= render_if_exists "registrations/welcome/setup_for_company", f: f = render_if_exists "registrations/welcome/setup_for_company", f: f
= render_if_exists "registrations/welcome/joining_project"
= render 'devise/shared/email_opted_in', f: f = render 'devise/shared/email_opted_in', f: f
= render_if_exists "registrations/welcome/jobs_to_be_done", f: f
.row .row
.form-group.col-sm-12.gl-mb-0 .form-group.col-sm-12.gl-mb-0
- if partial_exists? "registrations/welcome/button" - if partial_exists? "registrations/welcome/button"
......
---
name: bypass_registration
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72827
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/340560
milestone: '14.5'
type: experiment
group: group::adoption
default_enabled: false
import mountProgressBar from 'ee/registrations/projects/new';
import initProjectVisibilitySelector from '~/project_visibility'; import initProjectVisibilitySelector from '~/project_visibility';
import initProjectNew from '~/projects/project_new'; import initProjectNew from '~/projects/project_new';
mountProgressBar();
initProjectVisibilitySelector(); initProjectVisibilitySelector();
initProjectNew.bindEvents(); initProjectNew.bindEvents();
import mountProgressBar from 'ee/registrations/welcome'; import 'ee/registrations/welcome/other_role';
import 'ee/registrations/welcome/jobs_to_be_done';
mountProgressBar();
import mountProgressBar from 'ee/registrations/welcome'; import 'ee/registrations/welcome/other_role';
import 'ee/registrations/welcome/jobs_to_be_done';
mountProgressBar();
const emailUpdatesForm = document.querySelector('.js-email-opt-in'); const emailUpdatesForm = document.querySelector('.js-email-opt-in');
const setupForCompany = document.querySelector('.js-setup-for-company'); const setupForCompany = document.querySelector('.js-setup-for-company');
......
...@@ -8,7 +8,3 @@ export const STEPS = { ...@@ -8,7 +8,3 @@ export const STEPS = {
}; };
export const SUBSCRIPTON_FLOW_STEPS = [STEPS.yourProfile, STEPS.checkout, STEPS.yourGroup]; export const SUBSCRIPTON_FLOW_STEPS = [STEPS.yourProfile, STEPS.checkout, STEPS.yourGroup];
export const SIGNUP_ONBOARDING_FLOW_STEPS = [STEPS.yourProfile, STEPS.yourGroup, STEPS.yourProject];
export const COMBINED_SIGNUP_FLOW_STEPS = [STEPS.yourProfile, STEPS.yourProject];
import Vue from 'vue'; import Vue from 'vue';
import mountVisibilityLevelDropdown from '~/groups/visibility_level'; import mountVisibilityLevelDropdown from '~/groups/visibility_level';
import 'ee/pages/trials/country_select'; import 'ee/pages/trials/country_select';
import ProgressBar from '../../components/progress_bar.vue';
import RegistrationTrialToggle from '../../components/registration_trial_toggle.vue'; import RegistrationTrialToggle from '../../components/registration_trial_toggle.vue';
import { STEPS, SIGNUP_ONBOARDING_FLOW_STEPS } from '../../constants';
function mountProgressBar() {
const el = document.getElementById('progress-bar');
if (!el) {
return null;
}
return new Vue({
el,
render(createElement) {
return createElement(ProgressBar, {
props: { steps: SIGNUP_ONBOARDING_FLOW_STEPS, currentStep: STEPS.yourGroup },
});
},
});
}
function toggleTrialForm(trial) { function toggleTrialForm(trial) {
const form = document.querySelector('.js-trial-form'); const form = document.querySelector('.js-trial-form');
...@@ -61,7 +42,6 @@ function mountTrialToggle() { ...@@ -61,7 +42,6 @@ function mountTrialToggle() {
} }
export default () => { export default () => {
mountProgressBar();
mountVisibilityLevelDropdown(); mountVisibilityLevelDropdown();
mountTrialToggle(); mountTrialToggle();
}; };
...@@ -2,7 +2,6 @@ import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils'; ...@@ -2,7 +2,6 @@ import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import $ from 'jquery'; import $ from 'jquery';
import { bindHowToImport } from '~/projects/project_new'; import { bindHowToImport } from '~/projects/project_new';
import { displayGroupPath, displayProjectPath } from './path_display'; import { displayGroupPath, displayProjectPath } from './path_display';
import mountProgressBar from './progress_bar';
import showTooltip from './show_tooltip'; import showTooltip from './show_tooltip';
const importButtonsSubmit = () => { const importButtonsSubmit = () => {
...@@ -35,7 +34,6 @@ const setAutofocus = () => { ...@@ -35,7 +34,6 @@ const setAutofocus = () => {
const mobileTooltipOpts = () => (bp.getBreakpointSize() === 'xs' ? { placement: 'bottom' } : {}); const mobileTooltipOpts = () => (bp.getBreakpointSize() === 'xs' ? { placement: 'bottom' } : {});
export default () => { export default () => {
mountProgressBar();
displayGroupPath('.js-group-path-source', '.js-group-path-display'); displayGroupPath('.js-group-path-source', '.js-group-path-display');
displayGroupPath('.js-import-group-path-source', '.js-import-group-path-display'); displayGroupPath('.js-import-group-path-source', '.js-import-group-path-display');
displayProjectPath('.js-project-path-source', '.js-project-path-display'); displayProjectPath('.js-project-path-source', '.js-project-path-display');
......
import Vue from 'vue';
import ProgressBar from '../../components/progress_bar.vue';
import { STEPS, COMBINED_SIGNUP_FLOW_STEPS } from '../../constants';
export default function mountProgressBar() {
const el = document.getElementById('progress-bar');
if (!el) {
return null;
}
return new Vue({
el,
render(createElement) {
return createElement(ProgressBar, {
props: { steps: COMBINED_SIGNUP_FLOW_STEPS, currentStep: STEPS.yourProject },
});
},
});
}
import Vue from 'vue';
import ProgressBar from '../../components/progress_bar.vue';
import { STEPS, SIGNUP_ONBOARDING_FLOW_STEPS } from '../../constants';
export default () => {
const el = document.getElementById('progress-bar');
if (!el) return null;
return new Vue({
el,
render(createElement) {
return createElement(ProgressBar, {
props: { steps: SIGNUP_ONBOARDING_FLOW_STEPS, currentStep: STEPS.yourProject },
});
},
});
};
import Vue from 'vue';
import 'ee/registrations/welcome/other_role';
import 'ee/registrations/welcome/jobs_to_be_done';
import { experiment } from '~/experimentation/utils';
import { parseBoolean } from '~/lib/utils/common_utils';
import ProgressBar from '../components/progress_bar.vue';
import {
STEPS,
SUBSCRIPTON_FLOW_STEPS,
SIGNUP_ONBOARDING_FLOW_STEPS,
COMBINED_SIGNUP_FLOW_STEPS,
} from '../constants';
export default () => {
const el = document.getElementById('progress-bar');
if (!el) return null;
const isInSubscriptionFlow = parseBoolean(el.dataset.isInSubscriptionFlow);
const isSignupOnboardingEnabled = parseBoolean(el.dataset.isSignupOnboardingEnabled);
let steps;
if (isInSubscriptionFlow) {
steps = SUBSCRIPTON_FLOW_STEPS;
} else if (isSignupOnboardingEnabled) {
experiment('combined_registration', {
use: () => {
steps = SIGNUP_ONBOARDING_FLOW_STEPS;
},
try: () => {
steps = COMBINED_SIGNUP_FLOW_STEPS;
},
});
}
return new Vue({
el,
render(createElement) {
return createElement(ProgressBar, {
props: { steps, currentStep: STEPS.yourProfile },
});
},
});
};
...@@ -95,6 +95,21 @@ module EE ...@@ -95,6 +95,21 @@ module EE
def combined_registration_experiment def combined_registration_experiment
experiment(:combined_registration, user: current_user) experiment(:combined_registration, user: current_user)
end end
override :update_success_path
def update_success_path
if params[:joining_project] == 'true'
bypass_registration_event(:joining_project)
path_for_signed_in_user(current_user)
else
bypass_registration_event(:creating_project)
experiment(:combined_registration, user: current_user).redirect_path(trial_params)
end
end
def bypass_registration_event(event_name)
experiment(:bypass_registration, user: current_user).track(event_name, user: current_user)
end
end end
end end
end end
...@@ -26,9 +26,19 @@ module EE ...@@ -26,9 +26,19 @@ module EE
end end
def registration_objective_options def registration_objective_options
localized_jobs_to_be_done_choices.merge( options = localized_jobs_to_be_done_choices.dup
joining_team: _('I’m joining my team who’s already on GitLab')
) experiment(:bypass_registration, user: current_user) do |e|
e.use do
options.merge(
joining_team: _('I’m joining my team who’s already on GitLab')
)
end
e.try do
options
end
e.run
end
end end
end end
end end
- return unless Gitlab.dev_env_or_com?
- experiment(:bypass_registration, user: current_user) do |e|
- e.publish_to_database
- e.try do
.row
.form-group.col-sm-12
= label_tag :joining_project, _('What would you like to do?'), class: 'label-bold'
.form-check.gl-mb-2
= radio_button_tag :joining_project, false, false, required: true, class: 'form-check-input'
= label_tag :joining_project_false, class: 'form-check-label normal' do
= _('Create a new project')
.form-text.gl-text-gray-600
= _('House your files, plan your work, collaborate on code, and more.')
.form-check
= radio_button_tag :joining_project, true, false, required: true, class: 'form-check-input'
= label_tag :joining_project_true, class: 'form-check-label normal' do
= _('Join a project')
.form-text.gl-text-gray-600
= _('Join your team on GitLab and contribute to an existing project')
...@@ -129,6 +129,7 @@ RSpec.describe Registrations::WelcomeController do ...@@ -129,6 +129,7 @@ RSpec.describe Registrations::WelcomeController do
describe '#update' do describe '#update' do
let(:setup_for_company) { 'false' } let(:setup_for_company) { 'false' }
let(:email_opted_in) { '0' } let(:email_opted_in) { '0' }
let(:joining_project) { 'false' }
subject(:update) do subject(:update) do
patch :update, params: { patch :update, params: {
...@@ -137,7 +138,8 @@ RSpec.describe Registrations::WelcomeController do ...@@ -137,7 +138,8 @@ RSpec.describe Registrations::WelcomeController do
setup_for_company: setup_for_company, setup_for_company: setup_for_company,
email_opted_in: email_opted_in, email_opted_in: email_opted_in,
registration_objective: 'code_storage' registration_objective: 'code_storage'
} },
joining_project: joining_project
} }
end end
...@@ -251,21 +253,43 @@ RSpec.describe Registrations::WelcomeController do ...@@ -251,21 +253,43 @@ RSpec.describe Registrations::WelcomeController do
allow(controller.helpers).to receive(:signup_onboarding_enabled?).and_return(true) allow(controller.helpers).to receive(:signup_onboarding_enabled?).and_return(true)
end end
context 'when combined_registration is candidate variant' do context 'when joining_project is "true"', :experiment do
before do let(:joining_project) { 'true' }
allow(controller).to receive(:experiment).and_call_original
stub_experiments(combined_registration: :candidate) it { is_expected.to redirect_to dashboard_projects_path }
end
it 'creates a "joining_project" experiment track event' do
expect(experiment(:bypass_registration)).to track(:joining_project, user: user).on_next_instance
it { is_expected.to redirect_to new_users_sign_up_groups_project_path } subject
end
end
it "doesn't call the force_company_trial experiment" do context 'when joining_project is "false"', :experiment do
expect(controller).not_to receive(:experiment).with(:force_company_trial, user: user) it 'creates a "creating_project" experiment track event' do
expect(experiment(:bypass_registration)).to track(:creating_project, user: user).on_next_instance
subject subject
end end
end end
context 'when joining_project is "false"' do
context 'when combined_registration is candidate variant' do
before do
allow(controller).to receive(:experiment).and_call_original
stub_experiments(combined_registration: :candidate)
end
it { is_expected.to redirect_to new_users_sign_up_groups_project_path }
it "doesn't call the force_company_trial experiment" do
expect(controller).not_to receive(:experiment).with(:force_company_trial, user: user)
subject
end
end
end
context 'and force_company_trial experiment is candidate' do context 'and force_company_trial experiment is candidate' do
let(:setup_for_company) { 'true' } let(:setup_for_company) { 'true' }
......
...@@ -17,12 +17,6 @@ RSpec.describe 'Combined registration flow', :js do ...@@ -17,12 +17,6 @@ RSpec.describe 'Combined registration flow', :js do
expect(page).to have_content('Welcome to GitLab') expect(page).to have_content('Welcome to GitLab')
page.within('.bar') do
expect(page).to have_content('Your profile')
expect(page).to have_content('Your first project')
expect(page).not_to have_content('Your GitLab group')
end
choose 'My company or team' choose 'My company or team'
click_on 'Continue' click_on 'Continue'
end end
...@@ -31,8 +25,6 @@ RSpec.describe 'Combined registration flow', :js do ...@@ -31,8 +25,6 @@ RSpec.describe 'Combined registration flow', :js do
let(:experiments) { { combined_registration: :candidate } } let(:experiments) { { combined_registration: :candidate } }
it 'A user can create a group and project' do it 'A user can create a group and project' do
expect(page).to have_content('Your first project')
page.within '.js-group-path-display' do page.within '.js-group-path-display' do
expect(page).to have_content('{group}') expect(page).to have_content('{group}')
end end
...@@ -59,8 +51,6 @@ RSpec.describe 'Combined registration flow', :js do ...@@ -59,8 +51,6 @@ RSpec.describe 'Combined registration flow', :js do
end end
it 'a user can create a group and import a project' do it 'a user can create a group and import a project' do
expect(page).to have_content('Your first project')
click_on 'Import' click_on 'Import'
page.within '.js-import-group-path-display' do page.within '.js-import-group-path-display' do
......
...@@ -14,15 +14,11 @@ RSpec.describe 'User sees new onboarding flow', :js do ...@@ -14,15 +14,11 @@ RSpec.describe 'User sees new onboarding flow', :js do
visit users_sign_up_welcome_path visit users_sign_up_welcome_path
expect(page).to have_content('Welcome to GitLab') expect(page).to have_content('Welcome to GitLab')
expect(page).to have_content('Your profile Your GitLab group Your first project')
expect(page).to have_css('li.current', text: 'Your profile')
choose 'Just me' choose 'Just me'
click_on 'Continue' click_on 'Continue'
expect(page).to have_content('Create your group') expect(page).to have_content('Create your group')
expect(page).to have_content('Your profile Your GitLab group Your first project')
expect(page).to have_css('li.current', text: 'Your GitLab group')
fill_in 'group_name', with: 'test' fill_in 'group_name', with: 'test'
...@@ -31,8 +27,6 @@ RSpec.describe 'User sees new onboarding flow', :js do ...@@ -31,8 +27,6 @@ RSpec.describe 'User sees new onboarding flow', :js do
click_on 'Create group' click_on 'Create group'
expect(page).to have_content('Create/import your first project') expect(page).to have_content('Create/import your first project')
expect(page).to have_content('Your profile Your GitLab group Your first project')
expect(page).to have_css('li.current', text: 'Your first project')
fill_in 'project_name', with: 'test' fill_in 'project_name', with: 'test'
......
...@@ -6,50 +6,21 @@ RSpec.describe 'Welcome screen', :js do ...@@ -6,50 +6,21 @@ RSpec.describe 'Welcome screen', :js do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
context 'when on GitLab.com' do context 'when on GitLab.com' do
let(:user_has_memberships) { false }
let(:in_subscription_flow) { false }
let(:in_trial_flow) { false }
before do before do
allow(Gitlab).to receive(:com?).and_return(true) allow(Gitlab).to receive(:com?).and_return(true)
gitlab_sign_in(user) gitlab_sign_in(user)
allow_any_instance_of(EE::WelcomeHelper).to receive(:user_has_memberships?).and_return(user_has_memberships) allow_any_instance_of(EE::WelcomeHelper).to receive(:user_has_memberships?).and_return(false)
allow_any_instance_of(EE::WelcomeHelper).to receive(:in_subscription_flow?).and_return(in_subscription_flow) allow_any_instance_of(EE::WelcomeHelper).to receive(:in_subscription_flow?).and_return(false)
allow_any_instance_of(EE::WelcomeHelper).to receive(:in_trial_flow?).and_return(in_trial_flow) allow_any_instance_of(EE::WelcomeHelper).to receive(:in_trial_flow?).and_return(false)
visit users_sign_up_welcome_path visit users_sign_up_welcome_path
end end
it 'shows the welcome page with a progress bar' do it 'shows the welcome page' do
expect(page).to have_content('Welcome to GitLab') expect(page).to have_content('Welcome to GitLab')
expect(page).to have_content('Your profile Your GitLab group Your first project')
expect(page).to have_content('Continue') expect(page).to have_content('Continue')
end end
context 'when in the subscription flow' do
let(:in_subscription_flow) { true }
it 'shows the progress bar with the correct steps' do
expect(page).to have_content('Your profile Checkout Your GitLab group')
end
end
context 'when user has memberships' do
let(:user_has_memberships) { true }
it 'does not show the progress bar' do
expect(page).not_to have_content('Your profile')
end
end
context 'when in the trial flow' do
let(:in_trial_flow) { true }
it 'does not show the progress bar' do
expect(page).not_to have_content('Your profile')
end
end
it 'allows specifying other for jobs_to_be_done' do it 'allows specifying other for jobs_to_be_done' do
expect(page).not_to have_content('Why are you signing up? (Optional)') expect(page).not_to have_content('Why are you signing up? (Optional)')
...@@ -86,18 +57,4 @@ RSpec.describe 'Welcome screen', :js do ...@@ -86,18 +57,4 @@ RSpec.describe 'Welcome screen', :js do
end end
end end
end end
context 'when not on GitLab.com' do
before do
allow(Gitlab).to receive(:com?).and_return(false)
gitlab_sign_in(user)
visit users_sign_up_welcome_path
end
it 'does not show the progress bar' do
expect(page).not_to have_content('Your profile')
expect(page).to have_content('Get started!')
end
end
end end
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe EE::RegistrationsHelper do RSpec.describe EE::RegistrationsHelper do
include Devise::Test::ControllerHelpers
describe '#signup_username_data_attributes' do describe '#signup_username_data_attributes' do
it 'has expected attributes' do it 'has expected attributes' do
expect(helper.signup_username_data_attributes.keys).to include(:api_path) expect(helper.signup_username_data_attributes.keys).to include(:api_path)
...@@ -21,5 +23,16 @@ RSpec.describe EE::RegistrationsHelper do ...@@ -21,5 +23,16 @@ RSpec.describe EE::RegistrationsHelper do
it '"other" is always the last option' do it '"other" is always the last option' do
expect(shuffled_options.last).to eq(['A different reason', 'other']) expect(shuffled_options.last).to eq(['A different reason', 'other'])
end end
context 'when the bypass_registration experiment is candidate', :experiment do
before do
stub_experiments({ bypass_registration: :candidate })
end
it "excludes the joining_team option" do
shuffled_option_values = shuffled_options.map { |item| item.last }
expect(shuffled_option_values).to contain_exactly(*UserDetail.registration_objectives.keys.reject {|k| k == "joining_team"})
end
end
end end
end end
...@@ -12,13 +12,16 @@ RSpec.describe 'registrations/welcome/show' do ...@@ -12,13 +12,16 @@ RSpec.describe 'registrations/welcome/show' do
allow(Gitlab).to receive(:com?).and_return(true) allow(Gitlab).to receive(:com?).and_return(true)
end end
describe 'forms and progress bar' do describe 'forms and progress bar', :experiments do
let_it_be(:user_other_role_details_enabled) { false } let_it_be(:user_other_role_details_enabled) { false }
let(:experiments) { {} }
before do before do
allow(view).to receive(:redirect_path).and_return(redirect_path) allow(view).to receive(:redirect_path).and_return(redirect_path)
allow(view).to receive(:signup_onboarding_enabled?).and_return(signup_onboarding_enabled) allow(view).to receive(:signup_onboarding_enabled?).and_return(signup_onboarding_enabled)
stub_feature_flags(user_other_role_details: user_other_role_details_enabled) stub_feature_flags(user_other_role_details: user_other_role_details_enabled)
stub_experiments(experiments)
render render
end end
...@@ -51,6 +54,16 @@ RSpec.describe 'registrations/welcome/show' do ...@@ -51,6 +54,16 @@ RSpec.describe 'registrations/welcome/show' do
it { is_expected_to_have_progress_bar(status: show_progress_bar) } it { is_expected_to_have_progress_bar(status: show_progress_bar) }
context 'bypass_registration experiment' do
it { is_expected.not_to have_selector('#joining_project_true') }
context 'when in the candidate variant' do
let(:experiments) { { bypass_registration: :candidate } }
it { is_expected.to have_selector('#joining_project_true') }
end
end
context 'feature flag other_role_details is enabled' do context 'feature flag other_role_details is enabled' do
let_it_be(:user_other_role_details_enabled) { true } let_it_be(:user_other_role_details_enabled) { true }
......
...@@ -9607,6 +9607,9 @@ msgstr "" ...@@ -9607,6 +9607,9 @@ msgstr ""
msgid "Create a new issue" msgid "Create a new issue"
msgstr "" msgstr ""
msgid "Create a new project"
msgstr ""
msgid "Create a new repository" msgid "Create a new repository"
msgstr "" msgstr ""
...@@ -16995,6 +16998,9 @@ msgstr "" ...@@ -16995,6 +16998,9 @@ msgstr ""
msgid "Hour (UTC)" msgid "Hour (UTC)"
msgstr "" msgstr ""
msgid "House your files, plan your work, collaborate on code, and more."
msgstr ""
msgid "Housekeeping" msgid "Housekeeping"
msgstr "" msgstr ""
...@@ -19808,6 +19814,12 @@ msgstr "" ...@@ -19808,6 +19814,12 @@ msgstr ""
msgid "Join Zoom meeting" msgid "Join Zoom meeting"
msgstr "" msgstr ""
msgid "Join a project"
msgstr ""
msgid "Join your team on GitLab and contribute to an existing project"
msgstr ""
msgid "Joined %{time_ago}" msgid "Joined %{time_ago}"
msgstr "" msgstr ""
...@@ -38542,6 +38554,9 @@ msgstr "" ...@@ -38542,6 +38554,9 @@ msgstr ""
msgid "What will you use this group for?" msgid "What will you use this group for?"
msgstr "" msgstr ""
msgid "What would you like to do?"
msgstr ""
msgid "What's new" msgid "What's new"
msgstr "" msgstr ""
......
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