Skip to content

Commit

Permalink
Merge branch 'master' into TAN-812-survey-other-option
Browse files Browse the repository at this point in the history
  • Loading branch information
EdwinKato committed Jan 30, 2024
2 parents 9d24124 + 7f0a1e4 commit 32e844e
Show file tree
Hide file tree
Showing 75 changed files with 807 additions and 305 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ jobs:
- run:
command: ssh << parameters.ssh_user >>@<< parameters.ssh_host >> -o StrictHostKeyChecking=no "docker pull citizenlabdotco/back-ee:$CIRCLE_BRANCH && docker run --env-file cl2-deployment/<< parameters.env_file >> citizenlabdotco/back-ee:$CIRCLE_BRANCH bin/rake db:migrate_if_pending cl2back:clean_tenant_settings email_campaigns:assure_campaign_records fix_existing_tenants:update_permissions cl2back:clear_cache_store email_campaigns:remove_deprecated"
- deploy:
command: ssh << parameters.ssh_user >>@<< parameters.ssh_host >> -o StrictHostKeyChecking=no "cd cl2-deployment && docker stack deploy --compose-file << parameters.compose_file >> << parameters.stack_name >> --with-registry-auth"
command: ssh << parameters.ssh_user >>@<< parameters.ssh_host >> -o StrictHostKeyChecking=no "cd cl2-deployment && docker stack deploy --compose-file << parameters.compose_file >> << parameters.stack_name >> --with-registry-auth --prune"

back-generate-tenant-templates:
resource_class: small
Expand Down
4 changes: 4 additions & 0 deletions back/app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,10 @@ def project_moderator?(project_id = nil)
project_id ? moderatable_project_ids.include?(project_id) : moderatable_project_ids.present?
end

def project_or_folder_moderator?
project_moderator? || project_folder_moderator?
end

def normal_user?
!admin? && moderatable_project_ids.blank? && moderated_project_folder_ids.blank?
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def input_filter_params(filter_params = params)
:votes_to,
:comments_from,
:comments_to,
:input_custom_field_no_empty_values,
*permitted_dynamic_keys,
**permitted_dynamic_array_keys
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def execute
inputs = analysis.inputs

inputs = filter_tags(inputs)
inputs = filter_input_custom_field_no_empty_values(inputs)
inputs = filter_published_at(inputs)
inputs = filter_reactions(inputs)
inputs = filter_comments(inputs)
Expand Down Expand Up @@ -44,6 +45,16 @@ def filter_tags(inputs)
end
end

def filter_input_custom_field_no_empty_values(inputs)
scope = inputs
if params[:input_custom_field_no_empty_values]
analysis.custom_fields.pluck(:key).each do |key|
scope = scope.where.not("custom_field_values->>'#{key}' IS NULL")
end
end
scope
end

def filter_published_at(inputs)
scope = inputs

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
"search": {
"type": "string"
},
"input_custom_field_no_empty_values": {
"type": "boolean"
},
"reactions_from": {
"type": "number"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
parameter :votes_to, 'Filter by number of votes on the input, smaller than or equal to', type: :integer
parameter :comments_from, 'Filter by number of comments on the input, larger than or equal to', type: :integer
parameter :comments_to, 'Filter by number of comments on the input, smaller than or equal to', type: :integer
parameter :input_custom_field_no_empty_values, 'Filter out inputs with empty values for custom fields', type: :boolean
end

let_it_be(:analysis) { create(:analysis) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,30 @@
end
end

describe 'input_custom_field_no_empty_values' do
let_it_be(:custom_form) { create(:custom_form) }
let_it_be(:custom_field_select) { create(:custom_field_select, :with_options, resource: custom_form) }
let_it_be(:analysis) { create(:analysis, custom_fields: [custom_field_select]) }

let_it_be(:input0) { create(:idea, project: analysis.source_project) }
let_it_be(:input1) do
create(:idea, project: analysis.source_project, custom_field_values: {
custom_field_select.key => custom_field_select.options[0].key
})
end

let_it_be(:input2) do
create(:idea, project: analysis.source_project, custom_field_values: {
custom_field_select.key => custom_field_select.options[1].key
})
end

it 'filters out custom_field with no empty values correctly' do
@params = { input_custom_field_no_empty_values: true }
expect(output).to contain_exactly(input1, input2)
end
end

describe 'input_custom_<uuid>from/to' do
let_it_be(:analysis) { create(:analysis) }
let_it_be(:custom_form) { create(:custom_form) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,19 @@ namespace :setup_and_support do # rubocop:disable Metrics/BlockLength
end
end

desc 'Change the default assignee for proposals'
task :proposals_default_assignee, %i[host email] => [:environment] do |_, args|
Apartment::Tenant.switch(args[:host].tr('.', '_')) do
user = User.find_by email: args[:email]
raise "No user found for email #{args[:email]}" if !user
raise "#{user.email} is not an admin" if !user.admin?
raise "#{user.email} is a super admin" if user.super_admin?

new_created_at = User.admin.order(:created_at).first.created_at - 1.day
user.update!(created_at: new_created_at)
end
end

def add_anonymous_reaction(reactable, mode)
attrs = AnonymizeUserService.new.anonymized_attributes AppConfiguration.instance.settings('core', 'locales')
attrs.delete 'custom_field_values'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,44 @@ def initialize(user, scope)
end

def resolve
raise Pundit::NotAuthorizedError unless user&.active? && user&.admin?

@scope.all
raise Pundit::NotAuthorizedError unless user&.active?

if user.admin?
@scope.all
elsif user.project_or_folder_moderator?
@scope.where(owner: user)
else
raise Pundit::NotAuthorizedError
end
end
end

def write?
record.phase? ? PhasePolicy.new(user, record.phase).update? : (admin? && active?)
return false unless active?

if record.phase?
PhasePolicy.new(user, record.phase).update?
elsif admin?
true
elsif user.project_or_folder_moderator?
record.owner == user
else
false
end
end

def read?
record.phase? ? PhasePolicy.new(user, record.phase).show? : (admin? && active?)
return false unless active?

if record.phase?
PhasePolicy.new(user, record.phase).show?
elsif admin?
true
elsif user.project_or_folder_moderator?
record.owner == user
else
false
end
end

alias show? read?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

let(:scope) { described_class::Scope.new(user, ReportBuilder::Report) }

context 'when the user has admin rights' do
context 'when user has admin rights' do
let_it_be(:user) { build(:admin) }

it { is_expected.to permit(:show) }
Expand All @@ -21,6 +21,50 @@
it { expect(scope.resolve.count).to eq(3) }
end

context 'when user has moderator rights' do
let_it_be(:user) { build(:project_moderator) }

context 'when user owns the report' do
let_it_be(:all_reports) { create_list(:report, 3) }
let_it_be(:report) do
all_reports.first.tap { |r| r.update!(owner: user) }
end

it { is_expected.to permit(:show) }
it { is_expected.to permit(:layout) }
it { is_expected.to permit(:create) }
it { is_expected.to permit(:destroy) }
it { is_expected.to permit(:update) }
it { expect(scope.resolve.count).to eq(1) }
end

context 'when user does not own the report' do
it { is_expected.not_to permit(:show) }
it { is_expected.not_to permit(:layout) }
it { is_expected.not_to permit(:create) }
it { is_expected.not_to permit(:destroy) }
it { is_expected.not_to permit(:update) }
it { expect(scope.resolve.count).to eq(0) }

context 'when report belongs to phase moderated by this user' do
let_it_be(:all_reports) { create_list(:report, 3) }
let_it_be(:report) do
project = Project.find(user.moderatable_project_ids.first)
phase = build(:phase)
project.update!(phases: [phase])
all_reports.first.tap { |r| r.update!(phase: phase) }
end

it { is_expected.to permit(:show) }
it { is_expected.to permit(:layout) }
it { is_expected.to permit(:create) }
it { is_expected.to permit(:destroy) }
it { is_expected.to permit(:update) }
it { expect(scope.resolve.count).to eq(0) }
end
end
end

context 'when user is a visitor' do
let_it_be(:user) { nil }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,20 @@ def current_user_by_unsubscription_token
if token
token.user
else
unless params[:unsubscription_token].nil?
# Temporarily report the exception to Sentry to help us debug issue TAN-887.
# TODO: It can be removed after 2024-03-25.
extra = {
user_id: current_user&.id,
unsubscription_token: params[:unsubscription_token],
campaign_id: params[:campaign_id]
}

ErrorReporter.report_msg(<<~MSG, extra: extra)
Unsubscription token could not be found.
MSG
end

current_user
end
end
Expand Down
2 changes: 1 addition & 1 deletion back/engines/free/email_campaigns/config/locales/nl-BE.yml
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ nl-BE:
cta_see_results: 'Bekijk resultaten op het platform'
event_registration_confirmation:
subject: "Je bent binnen! Je inschrijving voor \"%{eventTitle}\" is bevestigd"
header_message: "%{firstName}Bedankt voor het registreren voor"
header_message: "%{firstName}, bedankt voor het registreren voor"
event_details:
labels:
date: 'Datum'
Expand Down
2 changes: 1 addition & 1 deletion back/engines/free/email_campaigns/config/locales/nl-NL.yml
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ nl:
cta_see_results: 'Bekijk resultaten op het platform'
event_registration_confirmation:
subject: "Je bent binnen! Je inschrijving voor \"%{eventTitle}\" is bevestigd"
header_message: "%{firstName}Bedankt voor het registreren voor"
header_message: "%{firstName}, bedankt voor het registreren voor"
event_details:
labels:
date: 'Datum'
Expand Down
21 changes: 19 additions & 2 deletions back/spec/models/user_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -558,17 +558,34 @@
expect(u.project_folder_moderator?(l2.id)).to be false
end

it 'response true when the user is project_folder_moderator and no project_folder_id is passed' do
it 'responds true when the user is project_folder_moderator and no project_folder_id is passed' do
u = build(:user, roles: [{ type: 'project_folder_moderator', project_folder_id: 'project_folder_id' }])
expect(u.project_folder_moderator?).to be true
end

it 'response false when the user is not a project_folder_moderator and no project_folder_id is passed' do
it 'responds false when the user is not a project_folder_moderator and no project_folder_id is passed' do
u = build(:admin)
expect(u.project_folder_moderator?).to be false
end
end

describe 'project_or_folder_moderator?' do
it 'responds true when the user has the project_folder_moderator role' do
u = build(:user, roles: [{ type: 'project_folder_moderator', project_folder_id: 'project_folder_id' }])
expect(u.project_or_folder_moderator?).to be true
end

it 'responds true when the user is project_moderator' do
u = build(:user, roles: [{ type: 'project_moderator', project_id: 'project_id' }])
expect(u.project_or_folder_moderator?).to be true
end

it 'responds false when the user does not have any moderator roles' do
u = build(:user, roles: [])
expect(u.project_or_folder_moderator?).to be false
end
end

describe 'moderator scopes' do
let(:user) { create(:user) }
let(:admin) { create(:admin) }
Expand Down
2 changes: 1 addition & 1 deletion cl2-component-library/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "0.11.27",
"version": "0.11.28",
"license": "MIT",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
Expand Down
9 changes: 9 additions & 0 deletions cl2-component-library/src/components/Icon/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,15 @@ export const icons = {
<path fill="none" d="M0 0h140.87v163.324H0z" />
</Svg>
),
comment: (props: IconPropsWithoutName) => (
<svg
className={`cl-icon ${props.className ? props.className : ''}`}
viewBox="0 0 24 24"
{...props}
>
<path d="M21.99 4c0-1.1-.89-2-1.99-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4z" />
</svg>
),
comments: (props: IconPropsWithoutName) => (
<Svg
className={`cl-icon ${props.className ? props.className : ''}`}
Expand Down
1 change: 1 addition & 0 deletions front/app/api/analysis_inputs/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export type IInputsFilterParams = {
votes_to?: string | number;
comments_from?: string | number;
comments_to?: string | number;
input_custom_field_no_empty_values?: boolean;
} & { [K in AuthorCustomFromFilterKey]?: string } & {
[K in AuthorCustomToFilterKey]?: string;
} & { [K in AuthorCustomInFilterKey]?: string[] } & {
Expand Down
7 changes: 2 additions & 5 deletions front/app/api/campaign_consents/useCampaignConsents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,11 @@ import {

const fetchCampaignConsents = (consentsRequestData?: IConsentsRequestData) => {
return fetcher<ICampaignConsents>({
path: `/consents${
typeof consentsRequestData?.unsubscriptionToken === 'string'
? `?unsubscription_token=${consentsRequestData.unsubscriptionToken}`
: ''
}`,
path: `/consents`,
action: 'get',
queryParams: {
without_campaign_names: consentsRequestData?.withoutCampaignNames,
unsubscription_token: consentsRequestData?.unsubscriptionToken,
},
});
};
Expand Down
11 changes: 5 additions & 6 deletions front/app/api/campaign_consents/useUpdateCampaignConsents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@ const updateCampaignConsents = async ({
const idPart = campaignConsentId
? campaignConsentId
: `by_campaign_id/${campaignId}`;
const tokenPart =
typeof unsubscriptionToken === 'string'
? `?unsubscription_token=${unsubscriptionToken}`
: '';
return fetcher<ICampaignConsent>({
path: `/consents/${idPart}${tokenPart}`,
path: `/consents/${idPart}`,
action: 'patch',
body: { consent: { consented } },
body: {
consent: { consented },
unsubscription_token: unsubscriptionToken,
},
});
})
);
Expand Down
Loading

0 comments on commit 32e844e

Please sign in to comment.