diff --git a/app/app/urls.py b/app/app/urls.py index c387116562d..36ef3ab4b3d 100644 --- a/app/app/urls.py +++ b/app/app/urls.py @@ -260,7 +260,6 @@ path('issue/payout', dashboard.views.payout_bounty, name='payout_bounty'), path('issue/increase', dashboard.views.increase_bounty, name='increase_bounty'), path('issue/cancel', dashboard.views.cancel_bounty, name='kill_bounty'), - path('issue/refund_request', dashboard.views.refund_request, name='refund_request'), path('issue/cancel_reason', dashboard.views.cancel_reason, name='cancel_reason'), path('modal/social_contribution', dashboard.views.social_contribution_modal, name='social_contribution_modal'), path( @@ -566,11 +565,6 @@ faucet.views.process_faucet_request, name='process_faucet_request' ), - re_path( - r'^_administration/process_refund_request/(.*)$', - dashboard.views.process_refund_request, - name='process_refund_request' - ), re_path( r'^_administration/email/start_work_approved$', retail.emails.start_work_approved, name='start_work_approved' ), diff --git a/app/assets/v2/js/pages/process_refund_request.js b/app/assets/v2/js/pages/process_refund_request.js deleted file mode 100644 index 3b34a1cf175..00000000000 --- a/app/assets/v2/js/pages/process_refund_request.js +++ /dev/null @@ -1,106 +0,0 @@ -$(document).ready(function() { - - load_tokens(); - $('#rejectRefund').prop('disabled', true); - - $('#reject_comments').on('change keyup paste', function() { - if ($(this).val().length > 0) - $('#rejectRefund').prop('disabled', false); - else - $('#rejectRefund').prop('disabled', true); - }); - - $('#rejectRefund').on('click', function(e) { - e.preventDefault(); - const data = { - 'comment': $('#reject_comments').val() - }; - - handleRequest(data); - }); - - $('#approveRefund').on('click', function(e) { - e.preventDefault(); - $('#approveRefund').attr('disabled', 'disabled'); - - $('#loadingImg').show(); - const from = web3.eth.coinbase; - const to = $('#destination-addr').html(); - const amount = parseFloat($('#amount').html()); - const token = $('#token').html(); - const gasPrice = web3.toHex(document.gas_price * Math.pow(10, 9)); - - console.log(from, 'from -> to', to); - if (token == 'ETH') { - web3.eth.sendTransaction({ - from: from, - to: to, - value: web3.toWei(amount, 'ether'), - gasPrice: gasPrice - }, function(error, txnId) { - if (error) { - console.log ('Unable to refund bounty fee. Please try again.', error); - $('#errResponse').show(); - $('#loadingImg').hide(); - } else { - $('#sucessResponse').show(); - $('#loadingImg').hide(); - console.log('transaction', txnId); - const data = { - txnId: txnId, - fulfill: true - }; - - handleRequest(data); - } - }); - } else { - // ERC 20 token - const _token = tokenNameToDetails(document.web3network, token); - const amountInWei = amount * 1.0 * Math.pow(10, _token.decimals); - const token_contract = web3.eth.contract(token_abi).at(_token.addr); - - token_contract.transfer(to, amountInWei, { gasPrice: gasPrice }, - function(error, txnId) { - if (error) { - console.log ('Unable to refund bounty fee. Please try again.', error); - $('#errResponse').show(); - $('#loadingImg').hide(); - } else { - $('#sucessResponse').show(); - $('#loadingImg').hide(); - console.log('transaction', txnId); - const data = { - txnId: txnId, - fulfill: true - }; - - handleRequest(data); - } - } - ); - } - - }); -}); - - -const handleRequest = (data) => { - let csrftoken = $("input[name='csrfmiddlewaretoken']").val(); - - $.ajax({ - type: 'post', - url: '', - data: data, - headers: {'X-CSRFToken': csrftoken}, - success: () => { - if (data.comment) - alert('Request Reject. You may now close this window'); - else - alert('Request Processed. You may now close this window'); - }, - error: () => { - _alert({ message: 'Something went wrong submitting your request. Please try again.'}, 'error'); - } - }); -}; \ No newline at end of file diff --git a/app/assets/v2/js/pages/refund_request.js b/app/assets/v2/js/pages/refund_request.js deleted file mode 100644 index ccb5bfcd1b3..00000000000 --- a/app/assets/v2/js/pages/refund_request.js +++ /dev/null @@ -1,39 +0,0 @@ -window.addEventListener('load', function() { - setInterval(listen_for_web3_changes, 5000); - listen_for_web3_changes(); -}); - -$(document).ready(function() { - if (!caseInsensitiveCompare($('#bounty_owner').html(), document.contxt.github_handle)) { - $('.metamask-banner').hide(); - $('#primary_form').hide(); - $('#wrong_owner').show(); - } else { - $('#primary_form').show(); - } - - $('#submitRequest').on('click', function(event) { - event.preventDefault(); - let csrftoken = $("input[name='csrfmiddlewaretoken']").val(); - - const data = { - 'comment': $('#refund_reason').val() - }; - - $.ajax({ - type: 'post', - url: '?pk=' + $('#pk').val(), - data: data, - headers: {'X-CSRFToken': csrftoken}, - success: () => { - $('#primary_form').hide(); - $('#success_container').show(); - }, - error: (e) => { - console.log(e); - - _alert({ message: 'Something went wrong submitting your request. Please try again.'}, 'error'); - } - }); - }); -}); \ No newline at end of file diff --git a/app/assets/v2/js/shared.js b/app/assets/v2/js/shared.js index b22b6de3e72..896ff15549c 100644 --- a/app/assets/v2/js/shared.js +++ b/app/assets/v2/js/shared.js @@ -886,7 +886,7 @@ var trigger_faucet_form_web3_hooks = function() { $('#faucet_form').removeClass('hidden'); } } - if ($('#admin_faucet_form').length || $('#admin_refund_form').length) { + if ($('#admin_faucet_form').length) { if (typeof web3 == 'undefined') { $('#no_metamask_error').css('display', 'block'); $('#faucet_form').addClass('hidden'); diff --git a/app/dashboard/admin.py b/app/dashboard/admin.py index e8a548c0111..3e822c27145 100644 --- a/app/dashboard/admin.py +++ b/app/dashboard/admin.py @@ -26,9 +26,8 @@ from .models import ( Activity, BlockedURLFilter, BlockedUser, Bounty, BountyEvent, BountyFulfillment, BountyInvites, BountySyncRequest, CoinRedemption, CoinRedemptionRequest, Coupon, Earning, FeedbackEntry, HackathonEvent, HackathonProject, - HackathonRegistration, HackathonSponsor, Interest, LabsResearch, PortfolioItem, Profile, ProfileView, - RefundFeeRequest, SearchHistory, Sponsor, Tip, TipPayout, TokenApproval, Tool, ToolVote, TribeMember, UserAction, - UserVerificationModel, + HackathonRegistration, HackathonSponsor, Interest, LabsResearch, PortfolioItem, Profile, ProfileView, SearchHistory, + Sponsor, Tip, TipPayout, TokenApproval, Tool, ToolVote, TribeMember, UserAction, UserVerificationModel, ) @@ -309,50 +308,6 @@ def coupon_link(self, instance): return mark_safe(f"{copy}") -class RefundFeeRequestAdmin(admin.ModelAdmin): - """Setup the RefundFeeRequest admin results display.""" - - raw_id_fields = ['bounty', 'profile'] - ordering = ['-created_on'] - list_display = ['pk', 'created_on', 'fulfilled', 'rejected', 'link', 'get_bounty_link', 'get_profile_handle',] - readonly_fields = ['pk', 'token', 'fee_amount', 'comment', 'address', 'txnId', 'link', 'get_bounty_link',] - search_fields = ['created_on', 'fulfilled', 'rejected', 'bounty', 'profile'] - - def get_bounty_link(self, obj): - bounty = getattr(obj, 'bounty', None) - url = bounty.url - return mark_safe(f"{bounty}") - - def get_profile_handle(self, obj): - """Get the profile handle.""" - profile = getattr(obj, 'profile', None) - if profile and profile.handle: - return mark_safe( - f'{profile.handle}' - ) - if obj.github_username: - return obj.github_username - return 'N/A' - - get_profile_handle.admin_order_field = 'handle' - get_profile_handle.short_description = 'Profile Handle' - - def link(self, instance): - """Handle refund fee request specific links. - - Args: - instance (RefundFeeRequest): The refund request to build a link for. - - Returns: - str: The HTML element for the refund request link. - - """ - if instance.fulfilled or instance.rejected: - return 'n/a' - return mark_safe(f"process me") - link.allow_tags = True - - class HackathonSponsorAdmin(admin.ModelAdmin): """The admin object for the HackathonSponsor model.""" @@ -483,6 +438,5 @@ class TribeMemberAdmin(admin.ModelAdmin): admin.site.register(FeedbackEntry, FeedbackAdmin) admin.site.register(LabsResearch) admin.site.register(UserVerificationModel, VerificationAdmin) -admin.site.register(RefundFeeRequest, RefundFeeRequestAdmin) admin.site.register(Coupon, CouponAdmin) admin.site.register(TribeMember, TribeMemberAdmin) diff --git a/app/dashboard/models.py b/app/dashboard/models.py index b682422177a..c4365ea8238 100644 --- a/app/dashboard/models.py +++ b/app/dashboard/models.py @@ -1399,32 +1399,6 @@ class BountySyncRequest(SuperModel): processed = models.BooleanField() -class RefundFeeRequest(SuperModel): - """Define the Refund Fee Request model.""" - profile = models.ForeignKey( - 'dashboard.Profile', - null=True, - on_delete=models.SET_NULL, - related_name='refund_requests', - ) - bounty = models.ForeignKey( - 'dashboard.Bounty', - on_delete=models.CASCADE - ) - fulfilled = models.BooleanField(default=False) - rejected = models.BooleanField(default=False) - comment = models.TextField(max_length=500, blank=True) - comment_admin = models.TextField(max_length=500, blank=True) - fee_amount = models.FloatField() - token = models.CharField(max_length=10) - address = models.CharField(max_length=255) - txnId = models.CharField(max_length=255, blank=True) - - def __str__(self): - """Return the string representation of RefundFeeRequest.""" - return f"bounty: {self.bounty}, fee: {self.fee_amount}, token: {self.token}. Time: {self.created_on}" - - class Subscription(SuperModel): email = models.EmailField(max_length=255) diff --git a/app/dashboard/templates/bounty/process_refund_request.html b/app/dashboard/templates/bounty/process_refund_request.html deleted file mode 100644 index 87272fedbde..00000000000 --- a/app/dashboard/templates/bounty/process_refund_request.html +++ /dev/null @@ -1,164 +0,0 @@ -{% extends "admin/base_site.html" %} -{% comment %} - Copyright (C) 2020 Gitcoin Core - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - -{% endcomment %} - -{% load i18n static %} - -{% block extrastyle %}{{ block.super }}{% endblock %} - -{% block bodyclass %}{{ block.super }} dashboard{% endblock %} - -{% block breadcrumbs %}{% endblock %} - -{% block content %} - - -
-

{% trans "Process Refund Fee Request" %} {{obj.pk}}

- -
- - - -
- -
-
-
{% trans "Github Profile" %}:
- @{{ obj.profile.username }} - -
-
-
{% trans "Bounty" %}:
- {{ obj.bounty.get_absolute_url }} - -
-
-
{% trans "Fee" %}:
-

{{ obj.fee_amount }}

-
-
-
{% trans "Token" %}:
-

{{ obj.token }}

-
-
-
{% trans "Address" %}:
-

{{ obj.address }}

-
-
-
{% trans "Reason for Refund Request" %}:
-

{% if obj.comment %} {{ obj.comment }} {% else %} - {% endif %}

-
-
- - - - -
- {% csrf_token %} - -

{% blocktrans %}By approving this refund request, you will transfer {{ obj.fee }} {{ obj.token }} to - {{ obj.github_username }} with a wallet address of {{ obj.address }} for bounty {{obj.bounty}} {% endblocktrans %}

-
- {{pk}} -
- -
-
-
Reject Refund Request:
-
- {% csrf_token %} - - -
-
- -{% endblock %} -{% block extrahead %}{{ block.super }} - -{% include 'shared/footer_scripts.html' %} - - - - - -{% endblock %} diff --git a/app/dashboard/templates/bounty/refund_request.html b/app/dashboard/templates/bounty/refund_request.html deleted file mode 100644 index a9f87365967..00000000000 --- a/app/dashboard/templates/bounty/refund_request.html +++ /dev/null @@ -1,159 +0,0 @@ -{% comment %} - Copyright (C) 2019 Gitcoin Core - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program 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 Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - -{% endcomment %} -{% load i18n static %} - - - - {% include 'shared/head.html' %} - {% include 'shared/cards_pic.html' %} - - - - {% include 'shared/tag_manager_2.html' %} - {% include 'shared/top_nav.html' with class='d-md-flex' %} -
- {% include 'shared/nav.html' %} -
-
-
- {% include 'shared/no_issue_error.html' with page='refund_request' %} - {% include 'shared/no_metamask_error.html' %} - {% include 'shared/zero_balance_error.html' %} - {% include 'shared/unlock_metamask.html' %} - {% include 'shared/connect_metamask.html' %} -
-
- {% if duplicate %} -
- -

{% trans "Ah a duplicate request!" %}

-

- {% trans "A request has already been raised for this bounty." %}
- {% trans "It's being reviewed by the Gitcoin Team." %} -

- - - {% trans "Back to Bounty" %} - -
- {% else %} -
-
-
-
- - -
- -

{% trans "You are not the chosen one." %}

-

- {% trans "Login as the funder to submit a fee refund request for this bounty." %} -

-
- -
-
-
- -
-
-

{% trans "Request Bounty Fee Refund" %}

-

{% trans "(Funder only)" %}

-
-
-

{% trans "If Gitcoin has charged you fee which shouldn't have been charged. Let us know and we'll look into it." %}

- {% csrf_token %} -
-
-

- {% trans "Back to Bounty" %} -

- -

{{ bounty.get_absolute_url }}

- -

{{ bounty.bounty_owner_github_username }}

- -

{{ bounty.bounty_owner_address }}

-
- -
-
{% trans "Reason" %}
-

{% trans "Please let us know why you felt extra fees were charged" %}

- -
- - -
- -
-
-
- -
-
{% trans "Request Received" %}
- {% include 'svgs/success.svg' %} -
-

- - - {% trans "Back to Bounty" %} - -

-

{% trans "Your fee refund request has been received." %}

-

{% trans "The Gitcoin team will review your request shortly," %}

-

{% trans "While you wait, why not create an *free* Gitcoin ENS name?" %}

- - {{ bounty.bounty_owner_github_username }}.gitcoin.eth is available. Click here to register it! - -
-
-
-
-
-
- {% endif %} - {% include 'shared/analytics.html' %} - {% include 'shared/footer_scripts.html' %} - {% include 'shared/footer.html' %} - - - - - - - - diff --git a/app/dashboard/views.py b/app/dashboard/views.py index ed2c2f862d0..8643ff209bc 100644 --- a/app/dashboard/views.py +++ b/app/dashboard/views.py @@ -99,8 +99,8 @@ from .models import ( Activity, BlockedURLFilter, Bounty, BountyEvent, BountyFulfillment, BountyInvites, CoinRedemption, CoinRedemptionRequest, Coupon, Earning, FeedbackEntry, HackathonEvent, HackathonProject, HackathonRegistration, - HackathonSponsor, Interest, LabsResearch, PortfolioItem, Profile, ProfileSerializer, ProfileView, RefundFeeRequest, - SearchHistory, Sponsor, Subscription, Tool, ToolVote, TribeMember, UserAction, UserVerificationModel, + HackathonSponsor, Interest, LabsResearch, PortfolioItem, Profile, ProfileSerializer, ProfileView, SearchHistory, + Sponsor, Subscription, Tool, ToolVote, TribeMember, UserAction, UserVerificationModel, ) from .notifications import ( maybe_market_tip_to_email, maybe_market_tip_to_github, maybe_market_tip_to_slack, maybe_market_to_email, @@ -1529,119 +1529,6 @@ def cancel_bounty(request): return TemplateResponse(request, 'bounty/cancel.html', params) -def refund_request(request): - """Request refund for bounty - - Args: - pk (int): The primary key of the bounty to be cancelled. - - Raises: - Http404: The exception is raised if no associated Bounty is found. - - Returns: - TemplateResponse: The request refund view. - - """ - - if request.method == 'POST': - is_authenticated = request.user.is_authenticated - profile = request.user.profile if is_authenticated and hasattr(request.user, 'profile') else None - bounty = Bounty.objects.get(pk=request.GET.get('pk')) - - if not profile or not bounty or profile.username != bounty.bounty_owner_github_username : - return JsonResponse({ - 'message': _('Only bounty funder can raise this request!') - }, status=401) - - comment = escape(strip_tags(request.POST.get('comment'))) - - review_req = RefundFeeRequest.objects.create( - profile=profile, - bounty=bounty, - comment=comment, - token=bounty.token_name, - address=bounty.bounty_owner_address, - fee_amount=bounty.fee_amount - ) - - # TODO: Send Mail - - return JsonResponse({'message': _('Request Submitted.')}, status=201) - - bounty = handle_bounty_views(request) - - if RefundFeeRequest.objects.filter(bounty=bounty).exists(): - params = get_context( - ref_object=bounty, - active='refund_request', - title=_('Request Bounty Refund'), - ) - params['duplicate'] = True - return TemplateResponse(request, 'bounty/refund_request.html', params) - - params = get_context( - ref_object=bounty, - user=request.user if request.user.is_authenticated else None, - active='refund_request', - title=_('Request Bounty Refund'), - ) - - return TemplateResponse(request, 'bounty/refund_request.html', params) - - -@staff_member_required -def process_refund_request(request, pk): - """Request refund for bounty - - Args: - pk (int): The primary key of the bounty to be cancelled. - - Raises: - Http404: The exception is raised if no associated Bounty is found. - - Returns: - TemplateResponse: Admin view for request refund view. - - """ - - try : - refund_request = RefundFeeRequest.objects.get(pk=pk) - except RefundFeeRequest.DoesNotExist: - raise Http404 - - if refund_request.fulfilled: - messages.info(request, 'refund request already fulfilled') - return redirect(reverse('admin:index')) - - if refund_request.rejected: - messages.info(request, 'refund request already rejected') - return redirect(reverse('admin:index')) - - if request.POST: - - if request.POST.get('fulfill'): - refund_request.fulfilled = True - refund_request.txnId = request.POST.get('txnId') - messages.success(request, 'fulfilled') - - else: - refund_request.comment_admin = request.POST.get('comment') - refund_request.rejected = True - messages.success(request, 'rejected') - - refund_request.save() - messages.info(request, 'Complete') - # TODO: send mail - return redirect('admin:index') - - context = { - 'obj': refund_request, - 'recommend_gas_price': round(recommend_min_gas_price_to_confirm_in_time(1), 1), - } - - return TemplateResponse(request, 'bounty/process_refund_request.html', context) - - def helper_handle_admin_override_and_hide(request, bounty): admin_override_and_hide = request.GET.get('admin_override_and_hide', False) if admin_override_and_hide: