diff --git a/hypha/apply/activity/services.py b/hypha/apply/activity/services.py new file mode 100644 index 0000000000000000000000000000000000000000..e91c5114570eb8032cb43f914e6c538bc7f26430 --- /dev/null +++ b/hypha/apply/activity/services.py @@ -0,0 +1,47 @@ +from .models import Activity + + +def get_related_actions_for_user(obj, user): + """Return Activity objects related to an object, esp. useful with + ApplicationSubmission and Project. + + Args: + obj: instance of a model class + user: user who these actions are visible to. + + Returns: + `Activity` queryset + """ + related_query = type(obj).activities.rel.related_query_name + + return ( + Activity.actions.filter(**{related_query: obj}) + .select_related('user') + .prefetch_related( + 'related_object', + ) + .visible_to(user) + ) + + +def get_related_comments_for_user(obj, user): + """Return comments/communications related to an object, esp. useful with + ApplicationSubmission and Project. + + Args: + obj: instance of a model class + user: user who these actions are visible to. + + Returns: + `Activity` queryset + """ + related_query = type(obj).activities.rel.related_query_name + + return ( + Activity.comments.filter(**{related_query: obj}) + .select_related('user') + .prefetch_related( + 'related_object', + ) + .visible_to(user) + ) diff --git a/hypha/apply/activity/views.py b/hypha/apply/activity/views.py index 1a1be200ff95ef1f74e4b8afec919bb099955455..caadfa4946a47d717ab41162e18f2fb7e588b8ad 100644 --- a/hypha/apply/activity/views.py +++ b/hypha/apply/activity/views.py @@ -9,25 +9,17 @@ from .filters import NotificationFilter from .forms import CommentForm from .messaging import MESSAGES, messenger from .models import COMMENT, Activity +from .services import get_related_comments_for_user class ActivityContextMixin: + """Mixin to add related 'comments' of the current view's 'self.object' + """ def get_context_data(self, **kwargs): - related_query = self.model.activities.rel.related_query_name - query = {related_query: self.object} extra = { # Do not prefetch on the related_object__author as the models # are not homogeneous and this will fail - 'actions': Activity.actions.filter(**query).select_related( - 'user', - ).prefetch_related( - 'related_object', - ).visible_to(self.request.user), - 'comments': Activity.comments.filter(**query).select_related( - 'user', - ).prefetch_related( - 'related_object', - ).visible_to(self.request.user), + 'comments': get_related_comments_for_user(self.object, self.request.user) } return super().get_context_data(**extra, **kwargs) diff --git a/hypha/apply/funds/templates/funds/applicationsubmission_detail.html b/hypha/apply/funds/templates/funds/applicationsubmission_detail.html index cba7ed59588dd43cad26d901d4aa4701b62625a6..08aba5edc0d387512315421fd259cd01188141c7 100644 --- a/hypha/apply/funds/templates/funds/applicationsubmission_detail.html +++ b/hypha/apply/funds/templates/funds/applicationsubmission_detail.html @@ -44,7 +44,16 @@ {% trans "Communications" %} </a> - <a class="tab__item" href="#activity-feed" data-tab="tab-3"> + <a class="tab__item" + href="#activity-feed" + hx-get="{% url 'funds:submissions:partial-activities' object.id %}" + hx-target="#tab-3 .feed" + hx-trigger="open-tab-3 once" + data-tab="tab-3" + x-data + @hashchange.window="location.hash === '#activity-feed' ? $dispatch('open-tab-3') : ''" + x-init="location.hash === '#activity-feed' ? $dispatch('open-tab-3') : ''" + > {% trans "Activity feed" %} </a> {% if request.user.is_apply_staff_admin %} @@ -58,7 +67,7 @@ </div> <div class="wrapper wrapper--large wrapper--tabs js-tabs-content"> -{# Tab 1 #} + {# Tab 1 #} <div class="tabs__content" id="tab-1"> {% block mobile_actions %} {% endblock %} @@ -169,7 +178,8 @@ {# Tab 3 #} <div class="tabs__content" id="tab-3"> <div class="feed"> - {% include "activity/include/action_list.html" %} + {% comment %} Loaded using the htmx via alpine's custom event "open-tab-3"{% endcomment %} + <p>Loading...</p> </div> </div> </div> diff --git a/hypha/apply/funds/urls.py b/hypha/apply/funds/urls.py index 0f4e7a03e8f977bf2511551d7c1876676bac398c..02962ba71d538b818f685a007df66860769a6a07 100644 --- a/hypha/apply/funds/urls.py +++ b/hypha/apply/funds/urls.py @@ -27,6 +27,7 @@ from .views import ( SubmissionStaffFlaggedView, SubmissionUserFlaggedView, ) +from .views_partials import partial_submission_activities revision_urls = ([ path('', RevisionListView.as_view(), name='list'), @@ -59,6 +60,7 @@ submission_urls = ([ ])), path('<int:pk>/', include([ path('', SubmissionDetailView.as_view(), name="detail"), + path('partial/activities/', partial_submission_activities, name="partial-activities"), path('edit/', SubmissionEditView.as_view(), name="edit"), path('sealed/', SubmissionSealedView.as_view(), name="sealed"), path('simplified/', SubmissionDetailSimplifiedView.as_view(), name="simplified"), diff --git a/hypha/apply/funds/views_partials.py b/hypha/apply/funds/views_partials.py new file mode 100644 index 0000000000000000000000000000000000000000..a7442eaebb74e0700201620bff9fa87df8ac380d --- /dev/null +++ b/hypha/apply/funds/views_partials.py @@ -0,0 +1,21 @@ +from django.contrib.auth.decorators import login_required +from django.shortcuts import get_object_or_404, render +from django.views.decorators.http import require_GET + +from hypha.apply.activity.services import ( + get_related_actions_for_user, +) +from hypha.apply.funds.permissions import has_permission + +from .models import ApplicationSubmission + + +@login_required +@require_GET +def partial_submission_activities(request, pk): + submission = get_object_or_404(ApplicationSubmission, pk=pk) + has_permission( + 'submission_view', request.user, object=submission, raise_exception=True + ) + ctx = {'actions': get_related_actions_for_user(submission, request.user)} + return render(request, 'activity/include/action_list.html', ctx) diff --git a/hypha/apply/projects/templates/application_projects/project_detail.html b/hypha/apply/projects/templates/application_projects/project_detail.html index e41078735d396ddba922ee4a76ba05d41d38f167..15d81d43c2cb742f28a237c7a5da6cb3f03d4d00 100644 --- a/hypha/apply/projects/templates/application_projects/project_detail.html +++ b/hypha/apply/projects/templates/application_projects/project_detail.html @@ -86,7 +86,18 @@ {% trans "Communications" %} </a> - <a class="tab__item" href="#activity-feed" data-tab="tab-3"> + <a + class="tab__item" + href="#activity-feed" + data-tab="tab-3" + hx-get="{% url 'apply:projects:partial-activities' object.id %}" + hx-target="#tab-3 .feed" + hx-trigger="open-tab-3 once" + data-tab="tab-3" + x-data + @hashchange.window="location.hash === '#activity-feed' ? $dispatch('open-tab-3') : ''" + x-init="location.hash === '#activity-feed' ? $dispatch('open-tab-3') : ''" + > {% trans "Activity Feed" %} </a> </div> @@ -204,7 +215,8 @@ {# Tab 3 #} <div class="tabs__content" id="tab-3"> <div class="feed"> - {% include "activity/include/action_list.html" with editable=False %} + {% comment %} Loaded using the htmx via alpine's custom event "open-tab-3"{% endcomment %} + <p>Loading...</p> </div> </div> </div> diff --git a/hypha/apply/projects/urls.py b/hypha/apply/projects/urls.py index 6c3b8f7a088372a93489c213f31a718727426d34..9099267152058d630ec479ce18f123db83647fd4 100644 --- a/hypha/apply/projects/urls.py +++ b/hypha/apply/projects/urls.py @@ -26,6 +26,7 @@ from .views import ( ReportUpdateView, VendorDetailView, VendorPrivateMediaView, + partial_project_activities, ) app_name = 'projects' @@ -37,6 +38,7 @@ urlpatterns = [ path('<int:pk>/', include([ path('', ProjectDetailView.as_view(), name='detail'), + path('partial/activities/', partial_project_activities, name="partial-activities"), path('edit/', ProjectApprovalFormEditView.as_view(), name="edit"), path('documents/<int:file_pk>/', ProjectPrivateMediaView.as_view(), name="document"), path('contract/<int:file_pk>/', ContractPrivateMediaView.as_view(), name="contract"), diff --git a/hypha/apply/projects/views/__init__.py b/hypha/apply/projects/views/__init__.py index fb7ca2a0aa473cfcff0e1bb381e5a58e763a025d..7520c2342b5002febf6f73117665d31eb0660f22 100644 --- a/hypha/apply/projects/views/__init__.py +++ b/hypha/apply/projects/views/__init__.py @@ -30,6 +30,7 @@ from .project import ( UploadContractView, UploadDocumentView, ) +from .project_partials import partial_project_activities from .report import ( ReportDetailView, ReportFrequencyUpdate, @@ -41,6 +42,7 @@ from .report import ( from .vendor import CreateVendorView, VendorDetailView, VendorPrivateMediaView __all__ = [ + 'partial_project_activities', 'ChangeInvoiceStatusView', 'SendForApprovalView', 'UploadDocumentView', diff --git a/hypha/apply/projects/views/project_partials.py b/hypha/apply/projects/views/project_partials.py new file mode 100644 index 0000000000000000000000000000000000000000..72e24dc8c72a22b0421e48e352d6beb126fb9bb8 --- /dev/null +++ b/hypha/apply/projects/views/project_partials.py @@ -0,0 +1,19 @@ +from django.contrib.auth.decorators import login_required +from django.shortcuts import get_object_or_404, render +from django.views.decorators.http import require_GET + +from hypha.apply.activity.services import ( + get_related_actions_for_user, +) + +from ..models.project import Project + + +@login_required +@require_GET +def partial_project_activities(request, pk): + project = get_object_or_404(Project, pk=pk) + ctx = { + 'actions': get_related_actions_for_user(project, request.user) + } + return render(request, 'activity/include/action_list.html', ctx)