diff --git a/app/cells/decidim/badges/show.erb b/app/cells/decidim/badges/show.erb new file mode 100644 index 0000000..3dd58a3 --- /dev/null +++ b/app/cells/decidim/badges/show.erb @@ -0,0 +1,17 @@ +
+ <%= icon "information-line" %> +

local modification

+
+

<%= t "decidim.gamification.title" %>

+

+ <%= t "decidim.gamification.description" %> + <%= link_to t("decidim.gamification.all_badges_link"), gamification_badges_path, class: "text-secondary underline" %> +

+
+
+ +
+ <% available_badges.each do |badge| %> + <%= cell "decidim/badge", model, badge: %> + <% end %> +
\ No newline at end of file diff --git a/app/cells/decidim/profile_cell.rb b/app/cells/decidim/profile_cell.rb new file mode 100644 index 0000000..e54d47b --- /dev/null +++ b/app/cells/decidim/profile_cell.rb @@ -0,0 +1,130 @@ +# frozen_string_literal: true + +module Decidim + class ProfileCell < Decidim::ViewModel + include Decidim::Core::Engine.routes.url_helpers + include Decidim::UserProfileHelper + include Decidim::AriaSelectedLinkToHelper + include Decidim::ViewHooksHelper + include ActiveLinkTo + + delegate :current_organization, :current_user, :user_groups_enabled?, to: :controller + delegate :avatar_url, :nickname, :personal_url, :followers_count, :users_followings, :officialized_as, to: :presented_profile + + TABS_ITEMS = { + activity: { icon: "bubble-chart-line", path: :profile_activity_path }, + badges: { icon: "award-line", path: :profile_badges_path }, + following: { icon: "eye-2-line", path: :profile_following_path }, + followers: { icon: "group-line", path: :profile_followers_path }, + groups: { icon: "team-line", path: :profile_groups_path }, + members: { icon: "contacts-line", path: :profile_members_path }, + conversations: { icon: "question-answer-line", path: :profile_conversations_path }, + proposals: { icon: "eye-2-line", path: :profile_proposals_path } + }.freeze + + def show + return render :invalid if profile_holder.blank? + return render :inaccessible if profile_holder.blocked? && current_user_logged_in? + + render :show + end + + def profile_holder + model + end + + private + + def presented_profile + present(profile_holder) + end + + def show_badge? + return if user_group? + + profile_holder.officialized? + end + + def officialization_text + translated_attribute(officialized_as).presence || t("decidim.profiles.show.officialized") + end + + def details_items + [{ icon: "account-pin-circle-line", text: nickname }].tap do |items| + items.append(icon: "link", text: personal_url, url: personal_url) if personal_url.present? + if (following_count = users_followings.size).positive? + items.append(icon: TABS_ITEMS[:following][:icon], text: t("decidim.following.following_count", count: following_count)) + end + items.append(icon: TABS_ITEMS[:followers][:icon], text: t("decidim.followers.followers_count", count: followers_count)) if profile_holder.followers_count.positive? + end + end + + def description + decidim_html_escape presented_profile.about.to_s + end + + def content_cell + context[:content_cell] + end + + def active_content + context[:active_content] + end + + def current_user_logged_in? + current_user && !current_user.admin? + end + + def own_profile? + current_user && current_user == profile_holder + end + + def manageable_group? + return false unless user_group? + + current_user && current_user.manageable_user_groups.include?(profile_holder) + end + + def tab_item(key) + values = TABS_ITEMS[key].dup + values[:path] = send(values[:path], nickname: profile_holder.nickname) + values[:text] = t(key, scope: "decidim.profiles.show") + values.merge!(extra_data[key]) if extra_data.has_key?(key) + values + end + + def user_tabs + items = [:activity].tap do |keys| + keys << :badges if current_organization.badges_enabled? + keys.append(:following, :followers) + keys << :groups if user_groups_enabled? + end + items.map { |key| tab_item(key) } + end + + def group_tabs + items = [:members].tap do |keys| + # keys.append(:badges, :followers, :proposals, :activity) + keys.append(:badges, :followers, :proposals) + keys << :conversations if manageable_group? + end + items.map { |key| tab_item(key) } + end + + def extra_data + @extra_data ||= {}.tap do |v| + if current_user && user_group? && (count = profile_holder.unread_messages_count_for(current_user)).positive? + v[:conversations] = { count: } + end + end + end + + def tab_items + user_group? ? group_tabs : user_tabs + end + + def user_group? + profile_holder.is_a?(Decidim::UserGroup) + end + end +end diff --git a/app/cells/decidim/proposals/show.erb b/app/cells/decidim/proposals/show.erb new file mode 100644 index 0000000..c7171df --- /dev/null +++ b/app/cells/decidim/proposals/show.erb @@ -0,0 +1,16 @@ +<%# see : https://github.com/decidim/decidim/blob/release/0.29-stable/decidim-proposals/app/views/decidim/proposals/proposals/_proposals.html.erb %> + +<% if proposals.empty? %> + <%= cell("decidim/announcement", t("decidim.proposals.proposals.proposals.empty")) %> +<% else %> +
+

<%= t("count", scope: "decidim.proposals.proposals.index", count: @proposals.count) %>

+
+ + +
+ <% @proposals.each do |proposal| %> + <%= render partial: "../views/decidim/proposals/proposals/proposal.html", locals: { proposal: proposal, card_size: card_size_for_view_mode(@view_mode) } %> + <% end %> +
+<% end %> \ No newline at end of file diff --git a/app/cells/decidim/proposals_cell.rb b/app/cells/decidim/proposals_cell.rb new file mode 100644 index 0000000..ebb5a8b --- /dev/null +++ b/app/cells/decidim/proposals_cell.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Decidim + class ProposalsCell < Decidim::ViewModel + include Decidim::CellsPaginateHelper + include Decidim::Core::Engine.routes.url_helpers + include Decidim::CardHelper + include Decidim::Proposals::ProposalsHelper + # include Decidim::Coauthorable + + # see https://github.com/decidim/decidim/blob/release/0.29-stable/decidim-proposals/app/controllers/decidim/proposals/proposals_controller.rb + def show + proposals + @view_mode = "grid" + render :show + end + + def proposals + @proposals ||= Decidim::Proposals::Proposal.joins(:coauthorships) + .where( + decidim_coauthorships: { + # decidim_author_type: "Decidim::UserBaseEntity", + decidim_user_group_id: profile_holder.id + } + ) + # .with_type(type_key: "proposal") + .not_hidden + .published + .not_withdrawn + # @proposals ||= Decidim::Proposals::Proposal.joins(:coauthorships) + # .where("decidim_coauthorships.decidim_user_group_id": user_group.id) + end + end +end diff --git a/app/controllers/decidim/profiles_controller.rb b/app/controllers/decidim/profiles_controller.rb new file mode 100644 index 0000000..1580acd --- /dev/null +++ b/app/controllers/decidim/profiles_controller.rb @@ -0,0 +1,130 @@ +# frozen_string_literal: true + +module Decidim + # The controller to handle the user's public profile page. + # + # i18n-tasks-use t('decidim.profiles.show.badges') + # i18n-tasks-use t('decidim.profiles.show.groups') + # i18n-tasks-use t('decidim.profiles.show.group_admins') + # i18n-tasks-use t('decidim.profiles.show.group_members') + class ProfilesController < Decidim::ApplicationController + include UserGroups + include Flaggable + include HasProfileBreadcrumb + + helper Decidim::Messaging::ConversationHelper + + helper_method :profile_holder, :active_content, :context_menu + + before_action :ensure_profile_holder + before_action :ensure_profile_holder_is_a_group, only: [:members] + before_action :ensure_profile_holder_is_a_user, only: [:groups, :following] + before_action :ensure_user_not_blocked + + def show + return redirect_to profile_members_path if profile_holder.is_a?(Decidim::UserGroup) + + redirect_to profile_activity_path(nickname: params[:nickname].downcase) + end + + def tooltip + render json: { data: cell("decidim/author", profile_holder.presenter).profile_minicard } + end + + def following + @content_cell = "decidim/following" + @title_key = "following" + render :show + end + + def followers + @content_cell = "decidim/followers" + @title_key = "followers" + render :show + end + + def badges + @content_cell = "decidim/badges" + @title_key = "badges" + render :show + end + + def groups + enforce_user_groups_enabled + + @content_cell = "decidim/groups" + @title_key = "groups" + render :show + end + + def members + enforce_user_groups_enabled + + @content_cell = "decidim/members" + @title_key = "members" + render :show + end + + def group_admins + enforce_permission_to :manage, :user_group, user_group: profile_holder + + @content_cell = "decidim/group_admins" + @title_key = "group_admins" + render :show + end + + def group_members + enforce_permission_to :manage, :user_group, user_group: profile_holder + + @content_cell = "decidim/group_members" + @title_key = "group_members" + render :show + end + + def group_invites + enforce_permission_to :manage, :user_group, user_group: profile_holder + + @content_cell = "decidim/group_invites" + @title_key = "group_invites" + render :show + end + + def activity + @content_cell = "decidim/user_activity" + @title_key = "activity" + render :show + end + + def proposals + enforce_user_groups_enabled + + @content_cell = "decidim/proposals" + @title_key = "proposals" + render :show + end + + private + + def ensure_user_not_blocked + raise ActionController::RoutingError, "Blocked User" if profile_holder&.blocked? && !current_user&.admin? + end + + def ensure_profile_holder_is_a_group + raise ActionController::RoutingError, "No user group with the given nickname" unless profile_holder.is_a?(Decidim::UserGroup) + end + + def ensure_profile_holder_is_a_user + raise ActionController::RoutingError, "No user with the given nickname" unless profile_holder.is_a?(Decidim::User) + end + + def ensure_profile_holder + raise ActionController::RoutingError, "No user or user group with the given nickname" if !profile_holder || profile_holder.nickname.blank? + end + + def profile_holder + return if params[:nickname].blank? + + @profile_holder ||= Decidim::UserBaseEntity.find_by("nickname = ? AND decidim_organization_id = ?", params[:nickname].downcase, current_organization.id) + end + end +end \ No newline at end of file diff --git a/app/packs/stylesheets/decidim/decidim_application.scss b/app/packs/stylesheets/decidim/decidim_application.scss index ae29123..61bcf7e 100644 --- a/app/packs/stylesheets/decidim/decidim_application.scss +++ b/app/packs/stylesheets/decidim/decidim_application.scss @@ -37,3 +37,12 @@ header { } } +/* BEGIN Group Profile Proposals List */ + +.profile__scaffold__bottom-content { + .card__list-list, .card__grid-grid { + @apply my-8; + } +} + +/* END Group Profile Proposals List */ diff --git a/config/locales/en.yml b/config/locales/en.yml index 0b337fd..67f686d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -5,6 +5,9 @@ en: assembly: fields: promoted: "Promoted" + profiles: + show: + proposals: "Proposals" layouts: decidim: data_consent: diff --git a/config/routes.rb b/config/routes.rb index 2a8b9dc..2c00702 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,6 +3,12 @@ require "sidekiq/web" require "sidekiq-scheduler/web" +Decidim::Core::Engine.routes.draw do + scope "/profiles/:nickname", format: false, constraints: { nickname: %r{[^/]+} } do + get "proposals", to: "profiles#proposals", as: "profile_proposals" + end +end + Rails.application.routes.draw do if Rails.application.secrets.puma[:health_check][:enabled] get "/stats", to: redirect { |_params, request| "http://#{request.host}:#{Rails.application.secrets.puma[:health_check][:port]}/stats?#{request.params.to_query}" } @@ -22,3 +28,4 @@ mount Decidim::Core::Engine => "/" end +