Skip to content

Commit

Permalink
Merge pull request #3728 from gitcoinco/private-repo
Browse files Browse the repository at this point in the history
Private repo
  • Loading branch information
danlipert authored Mar 26, 2019
2 parents f5d4728 + cdf21ac commit 55712fc
Show file tree
Hide file tree
Showing 28 changed files with 1,183 additions and 71 deletions.
5 changes: 5 additions & 0 deletions app/app/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@
dashboard.views.profile_job_opportunity,
name='profile_job_opportunity'
),
url(
r'^api/v0.1/bountydocument',
dashboard.views.bounty_upload_nda,
name='bounty_upload_nda'
),
url(r'^api/v0.1/faucet/save/?', faucet.views.save_faucet, name='save_faucet'),
url(r'^api/v0.1/', include(dbrouter.urls)),
url(r'^api/v0.1/', include(kdrouter.urls)),
Expand Down
11 changes: 10 additions & 1 deletion app/app/utils.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import email
import imaplib
import logging
import os
import re
import time
from hashlib import sha1
from secrets import token_hex

from django.conf import settings
from django.contrib.auth.models import User
Expand All @@ -17,7 +19,6 @@
import requests
from avatar.models import SocialAvatar
from avatar.utils import get_svg_templates, get_user_github_avatar_image
from dashboard.models import Profile
from geoip2.errors import AddressNotFoundError
from git.utils import _AUTH, HEADERS, get_user
from ipware.ip import get_real_ip
Expand Down Expand Up @@ -161,6 +162,7 @@ def setup_lang(request, user):
DoesNotExist: The exception is raised if no profile is found for the specified handle.
"""
from dashboard.models import Profile
profile = None
if user.is_authenticated and hasattr(user, 'profile'):
profile = user.profile
Expand All @@ -174,7 +176,14 @@ def setup_lang(request, user):
request.session.modified = True


def get_upload_filename(instance, filename):
salt = token_hex(16)
file_path = os.path.basename(filename)
return f"docs/{getattr(instance, '_path', '')}/{salt}/{file_path}"


def sync_profile(handle, user=None, hide_profile=True):
from dashboard.models import Profile
handle = handle.strip().replace('@', '').lower()
data = get_user(handle)
email = ''
Expand Down
24 changes: 24 additions & 0 deletions app/assets/v2/css/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -1654,3 +1654,27 @@ div.busyOverlay {
border: 0.25em solid currentColor;
border-radius: 100px;
}

.cta-blue {
background: #0D0764;
color: white;
padding: 1.8rem;
border-radius: 0.2rem;
}

.privaterepo-instructions {
background: #F8F8F8;
padding: 1rem;
}

@media (min-width: 768px) {
.privaterepo-instructions {
width: 75%;
}

}

.g-modal .modal-header,
.g-modal .modal-footer {
border: none;
}
9 changes: 8 additions & 1 deletion app/assets/v2/css/forms/checkbox.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
background: #fff;
border: 1px solid #dbdbdb;
border-radius: 2px;
content: url('/static/v2/images/check.svg');
content: '';
display: inline-flex;
justify-content: center;
height: 18px;
Expand All @@ -30,11 +30,18 @@
color: #D50000;
}

.form__checkbox input:disabled ~ .form__label::before {
background-color: #d0d0d0 !important;
border-color: #d0d0d0 !important;
cursor: not-allowed;
}

.form__checkbox label.error {
display: none !important;
}

.form__checkbox input:checked ~ .form__label::before {
background-color: #0D0764;
border-color: #0D0764;
content: url('/static/v2/images/check.svg');
}
4 changes: 2 additions & 2 deletions app/assets/v2/css/forms/select.css
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@

.form__select2 .select2-container {
display: inline-block !important;
width: 100% !important;
min-width: 100%;
height: 100%;
font-size: 14px;
}
Expand Down Expand Up @@ -161,4 +161,4 @@

.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
color: #ffffff;
}
}
Binary file added app/assets/v2/images/emails/private-repo-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/assets/v2/images/emails/private-repo-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/assets/v2/images/emails/private-repo-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/assets/v2/images/emails/private-repo-4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/assets/v2/images/repo-instructions.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/assets/v2/images/repo-settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
537 changes: 537 additions & 0 deletions app/assets/v2/images/robots-party.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions app/assets/v2/js/ajax-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
* Generic function to make an AJAX call avoid DRY
*
* ex:
* var getdata = fetchData ('/api/v0.1/data/','GET')
* $.when( getdata ).then( function ( response ){ return response })
* var getdata = fetchData('/api/v0.1/data/','GET')
* $.when(getdata).then(function(response){ return response })
*
* var sendForm = fetchData ( e.currentTarget.action,
* var sendForm = fetchData(e.currentTarget.action,
* e.currentTarget.method,
* $("#form-wallets").serialize()
* )
* $.when( sendForm ).then( function ( payback ){ return payback })
* $.when(sendForm).then(function(payback){ return payback })
*
*/

Expand Down
149 changes: 121 additions & 28 deletions app/assets/v2/js/pages/bounty_details.js
Original file line number Diff line number Diff line change
Expand Up @@ -633,51 +633,132 @@ var attach_override_status = function() {
});
};


var show_interest_modal = function() {
var self = this;
let modals = $('#modalInterest');
let modalBody = $('#modalInterest .modal-content');
let modalUrl = `/interest/modal?redirect=${window.location.pathname}&pk=${document.result['pk']}`;

modals.on('show.bs.modal', function() {
modalBody.load(modalUrl, ()=> {
if (document.result['repo_type'] === 'private') {
$('#nda-upload').show();
$('#issueNDA').prop('required', true);
document.result.unsigned_nda ? $('.nda-download-link').attr('href', document.result.unsigned_nda.doc) : $('#nda-upload').hide();
}

setTimeout(function() {
var url = '/interest/modal?redirect=' + window.location.pathname + '&pk=' + document.result['pk'];

$.get(url, function(newHTML) {
var modal = $(newHTML).appendTo('body').modal({
modalClass: 'modal add-interest-modal'
});

var actionPlanForm = modal.find('form#action_plan');
var issueMessage = actionPlanForm.find('#issue_message');
let actionPlanForm = $('#action_plan');
let issueMessage = $('#issue_message');

issueMessage.attr('placeholder', gettext('What steps will you take to complete this task? (min 30 chars)'));

modal.on('submit', function(event) {
actionPlanForm.on('submit', function(event) {
event.preventDefault();

var msg = issueMessage.val().trim();
let msg = issueMessage.val().trim();
let issueNDA = $('#issueNDA')[0].files;

if (!msg || msg.length < 30) {
_alert({message: gettext('Please provide an action plan for this ticket. (min 30 chars)')}, 'error');
return false;
}

if (typeof issueNDA[0] !== 'undefined') {
const formData = new FormData();

formData.append('docs', issueNDA[0]);
formData.append('doc_type', 'signed_nda');
const ndaSend = {
url: '/api/v0.1/bountydocument',
method: 'POST',
data: formData,
processData: false,
dataType: 'json',
contentType: false
};

$.ajax(ndaSend).done(function(response) {
_alert(response.message, 'info');
add_interest(document.result['pk'], {
issue_message: msg,
signed_nda: response.bounty_doc_id
}).then(success => {
if (success) {
$(self).attr('href', '/uninterested');
$(self).find('span').text(gettext('Stop Work'));
$(self).parent().attr('title', '<div class="tooltip-info tooltip-sm">' + gettext('Notify the funder that you will not be working on this project') + '</div>');
$.modal.close();
}
}).catch((error) => {
if (error.responseJSON.error === 'You may only work on max of 3 issues at once.')
return;
throw error;
});
}).fail(function(error) {
_alert(error, 'error');
});
} else {
add_interest(document.result['pk'], {
issue_message: msg
}).then(success => {
if (success) {
$(self).attr('href', '/uninterested');
$(self).find('span').text(gettext('Stop Work'));
$(self).parent().attr('title', '<div class="tooltip-info tooltip-sm">' + gettext('Notify the funder that you will not be working on this project') + '</div>');
$.modal.close();
}
}).catch((error) => {
if (error.responseJSON.error === 'You may only work on max of 3 issues at once.')
return;
throw error;
});
}

add_interest(document.result['pk'], {
issue_message: msg
}).then(success => {
if (success) {
$(self).attr('href', '/uninterested');
$(self).find('span').text(gettext('Stop Work'));
$(self).parent().attr('title', '<div class="tooltip-info tooltip-sm">' + gettext('Notify the funder that you will not be working on this project') + '</div>');
$.modal.close();
}
}).catch((error) => {
if (error.responseJSON.error === 'You may only work on max of 3 issues at once.')
return;
throw error;
});
});

});
});
modals.bootstrapModal('show');
};

const repoInstructions = () => {
let linkToSettings = `https://github.com/${document.result.github_org_name}/${document.result.github_repo_name}/settings/collaboration`;


let modalTmp = `
<div class="modal fade g-modal" id="exampleModalCenter" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body text-center center-block w-75">
<h5>
You have successfully approved the contributor to work on your bounty!
</h5>
<div>
<img src="${document.contxt.STATIC_URL}v2/images/repo-instructions.png" class="mw-100 my-4" alt="">
</div>
<p class="mb-4">Now you need to invite the contributor to your private repo on GitHub You can find it under <b>GitHub repository > Settings > Collaborators</b></p>
<div>
<img src="${document.contxt.STATIC_URL}v2/images/repo-settings.png" class="mw-100" alt="">
</div>
</div>
<div class="modal-footer justify-content-center">
<a href="${linkToSettings}" target="_blank" class="button button--primary"><i class="fab fa-github"></i> Go to Repo Settings</a>
</div>
</div>
</div>
</div>`;

$(modalTmp).bootstrapModal('show');

$(document, modalTmp).on('hidden.bs.modal', function(e) {
$('#exampleModalCenter').remove();
$(modalTmp).bootstrapModal('dispose');
});
};

var set_extended_time_html = function(extendedDuration, currentExpires) {
Expand Down Expand Up @@ -1055,7 +1136,8 @@ var do_actions = function(result) {
const _entry = {
enabled: true,
href: github_url,
text: gettext('View On Github') +
text: (result['repo_type'] === 'private' ? '<i class="fas fa-lock"></i> ' +
gettext('Private Repo') : gettext('View On Github')) +
(result['is_issue_closed'] ? gettext(' (Issue is closed)') : ''),
parent: 'right_actions',
title: gettext('View issue details and comments on Github'),
Expand Down Expand Up @@ -1266,6 +1348,10 @@ var pull_bounty_from_api = function() {
render_activity(result, results);

document.result = result;

if (typeof promptPrivateInstructions !== 'undefined' && result.repo_type === 'private') {
repoInstructions();
}
return;
}
}
Expand Down Expand Up @@ -1324,6 +1410,12 @@ const process_activities = function(result, bounty_activities) {
const fulfillment = meta.fulfillment || {};
const new_bounty = meta.new_bounty || {};
const old_bounty = meta.old_bounty || {};
const has_signed_nda = result.interested.map(interest => {
if (interest.profile.handle === _activity.profile.handle && interest.signed_nda) {
return interest.signed_nda.doc;
}
return false;
});
const has_pending_interest = !!result.interested.find(interest =>
interest.profile.handle === _activity.profile.handle && interest.pending);
const has_interest = !!result.interested.find(interest =>
Expand Down Expand Up @@ -1357,6 +1449,7 @@ const process_activities = function(result, bounty_activities) {
age: timeDifference(now, new Date(_activity.created)),
activity_type: _activity.activity_type,
status: _activity.activity_type === 'work_started' ? 'started' : 'stopped',
signed_nda: has_signed_nda,
uninterest_possible: uninterest_possible,
slash_possible: slash_possible,
approve_worker_url: meta.approve_worker_url,
Expand Down
Loading

0 comments on commit 55712fc

Please sign in to comment.