Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ PROJECT_GITHUB_REPOSITORY_URL=https://github.com/noisy/steemprojects.com/
PROJECT_SLUG_ON_PAGE=steemprojects
STEEM_ACCOUNT=
STEEM_POSTING_KEY=
FILESTACK_API_KEY=
16 changes: 0 additions & 16 deletions .travis.yml

This file was deleted.

5 changes: 4 additions & 1 deletion compose/postgres/restore.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,7 @@ fi
echo "beginning restore from $1"
echo "-------------------------"

pg_restore --verbose -h postgres -U $POSTGRES_USER --clean -d $POSTGRES_USER $BACKUPFILE
EXIT_CODE=0

pg_restore --verbose -h postgres -U $POSTGRES_USER --clean -d $POSTGRES_USER $BACKUPFILE || EXIT_CODE=$?
echo $EXIT_CODE
2 changes: 1 addition & 1 deletion fabfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ def restore_db(filename="tmp.sqlc"):
remote_filename,
))

docker_compose("run postgres restore {}".format(remote_filename))
print(docker_compose("run postgres restore {}".format(remote_filename)))

docker_compose("run postgres rm /backups/{}".format(remote_filename))
if ENV.name != "local":
Expand Down
94 changes: 84 additions & 10 deletions package/forms.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
import itertools
from urllib.error import HTTPError

from package.models import Category, Project, PackageExample, ProjectImage
from package.utils import prepare_thumbnails
from profiles.models import Account

from django.core.exceptions import ValidationError
from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
from django.forms.formsets import formset_factory
from django.forms.models import modelformset_factory
from django.forms.widgets import Textarea, TextInput
from django.http import HttpResponseForbidden
from django.template.defaultfilters import slugify
from floppyforms.__future__ import ModelForm

from package.models import Category, Project, PackageExample, ProjectImage
from package.utils import (
prepare_thumbnails,
download_file,
get_image_name,
get_file_subtype_from_url,
rename_file,
join_path_with_file_name,
delete_file_from_media,
cut_domain_name_from_url,
)
from profiles.models import Account


def package_help_text():
help_text = ""
Expand Down Expand Up @@ -195,14 +207,76 @@ def save(self, *args, **kwargs):
)


class ProjectImagesFormSet(BaseProjectImagesFormSet):
class ProjectImageUrlForm(forms.Form):

def __init__(self, project, queryset=None, *args, **kwargs):
self.project = project
url = forms.URLField()
project = forms.CharField()

super(ProjectImagesFormSet, self).__init__(queryset=queryset, *args, **kwargs)
def __init__(self, project, user_data, *args, **kwargs):
super(ProjectImageUrlForm, self).__init__(*args, **kwargs)
self.image_path = None
self.absolute_image_path = None
self.delete = False
self.user = user_data
self.fields["url"].widget = forms.HiddenInput()
self.fields["project"].widget = forms.HiddenInput()
self.fields["project"].initial = project

def clean(self):
cleaned_data = super(ProjectImageUrlForm, self).clean()
image_url = cleaned_data.get("url")
project = cleaned_data.get("project")
project_object = Project.objects.get(id=project)
if not self.user.profile.can_edit_package(project_object):
raise HttpResponseForbidden("permission denied")
self.delete = cleaned_data.get("DELETE")
if self.delete:
try:
self.absolute_image_path = cut_domain_name_from_url(image_url)
except AttributeError:
raise ValidationError("Processing error")
delete_file_from_media(self.absolute_image_path)
self.image_path = self.absolute_image_path.split("/", 1)[-1]
else:
try:
ProjectImage.assert_image(image_url)
except (AttributeError, HTTPError):
raise ValidationError("File is not image")

image_project_path = join_path_with_file_name("imgs", project)
dest_path = join_path_with_file_name(settings.MEDIA_ROOT, image_project_path)
uuid_name = download_file(image_url, dest_path)
file_type = get_file_subtype_from_url(image_url)
timestamp_name = get_image_name(file_type)
self.absolute_image_path = rename_file(dest_path, uuid_name, timestamp_name)
self.image_path = join_path_with_file_name(image_project_path, timestamp_name)
return cleaned_data

def save(self, *args, **kwargs):
project = Project.objects.get(id=self.cleaned_data['project'])
if self.delete:
ProjectImage.objects.filter(project=project, img=self.image_path).delete()
else:
ProjectImage.objects.create(project=project, img=self.image_path)
prepare_thumbnails(self.absolute_image_path)


BaseProjectImagesUrlFormSet = formset_factory(
form=ProjectImageUrlForm,
can_delete=True,
extra=0,
)


class ProjectImagesUrlFormSet(BaseProjectImagesUrlFormSet):

def __init__(self, project, user_data, *args, **kwargs):
self.project = project
self.user_data = user_data
super(ProjectImagesUrlFormSet, self).__init__(*args, **kwargs)

def get_form_kwargs(self, *args, **kwargs):
form_kwargs = super(ProjectImagesFormSet, self).get_form_kwargs(*args, **kwargs)
form_kwargs = super(ProjectImagesUrlFormSet, self).get_form_kwargs(*args, **kwargs)
form_kwargs.update({"project": self.project})
form_kwargs.update({"user_data": self.user_data})
return form_kwargs
10 changes: 7 additions & 3 deletions package/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from core.models import BaseModel
from package.repos import get_repo_for_repo_url
from package.signals import signal_fetch_latest_metadata
from package.utils import get_version, get_pypi_version, normalize_license
from package.utils import get_version, get_pypi_version, normalize_license, get_image_name, get_file_maintype_from_url
from profiles.models import Profile, Account

repo_url_help_text = settings.PACKAGINATOR_HELP_TEXT['REPO_URL']
Expand Down Expand Up @@ -442,7 +442,6 @@ def role_confirmed_by_project_owner(self):

role_confirmed_by_account = models.NullBooleanField(_("Role confirmed by team mate"), blank=True, default=None)


class Meta:
unique_together = ("account", "project")

Expand All @@ -452,7 +451,7 @@ def __str__(self):

def project_img_path(instance, filename):
_, ext = os.path.splitext(filename)
return 'imgs/{}/{}{}'.format(instance.project.pk, int(round(time.time()*1000)), ext)
return 'imgs/{}/{}'.format(instance.project.pk, get_image_name(ext))


class ProjectImage(BaseModel):
Expand All @@ -479,6 +478,11 @@ def image_tag_thumb(self):
def __str__(self):
return "Project: {}, Image: {}".format(self.project.name, self.img.name)

@staticmethod
def assert_image(image_url):
main_type = get_file_maintype_from_url(image_url)
return main_type == 'image'


class PackageExample(BaseModel):

Expand Down
68 changes: 63 additions & 5 deletions package/utils.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import logging
import os
import time
import urllib
import uuid
from distutils.version import LooseVersion as versioner
from os import makedirs
from os.path import join, split, exists, splitext
from PIL import Image
from distutils.version import LooseVersion as versioner

from requests.compat import quote

from PIL import Image
from django.conf import settings
from django.db import models

from requests.compat import quote

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -132,3 +134,59 @@ def crop_image(image, ratio):

box = (padding_x, padding_y, x - padding_x, y - padding_y)
return image.crop(box)


def download_file(file_url, dest_path, file_name=None):
if not file_name:
file_name = generate_unique_file_name()
file_path = join_path_with_file_name(dest_path, file_name)
makedirs(dest_path, exist_ok=True)
urllib.request.urlretrieve(file_url, file_path)
return file_name


def get_file_maintype_from_url(file_url):
return urllib.request.urlopen(file_url).info().get_content_maintype()


def get_file_subtype_from_url(file_url):
return urllib.request.urlopen(file_url).info().get_content_subtype()


def get_image_name(file_type):
return f"{int(round(time.time()*1000))}.{file_type}"


def generate_unique_file_name():
return str(uuid.uuid4())


def rename_file(file_dir, old_name, new_name):
file_path_old = join_path_with_file_name(file_dir, old_name)
file_path_new = join_path_with_file_name(file_dir, new_name)
os.rename(file_path_old, file_path_new)
return file_path_new


def join_path_with_file_name(path, file_name):
if not path.endswith('/'):
path += '/'
return f"{path}{file_name}"


def split_path_for_path_and_name(file_path):
return file_path.rsplit("/", 1)


def cut_domain_name_from_url(file_url):
return file_url.split("/", 3)[-1]


def delete_file_from_media(image_path):
folder_path, image_name = split_path_for_path_and_name(image_path)
image_name = image_name.split(".")[0]
for root, dirs, files in os.walk(folder_path):
for file in files:
if file.find(image_name) != -1:
os.remove(join(root, file))

16 changes: 8 additions & 8 deletions package/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@
from django.utils.html import escape
from django.views.decorators.csrf import csrf_exempt


from grid.models import Grid
from package.forms import PackageForm, PackageExampleForm, DocumentationForm, ProjectImagesFormSet
from package.forms import PackageForm, PackageExampleForm, DocumentationForm, ProjectImagesUrlFormSet
from package.forms import TeamMembersFormSet
from package.models import Category, Project, PackageExample, ProjectImage, TeamMembership
from package.repos import get_all_repos
from package.forms import TeamMembersFormSet
from profiles.models import Account, AccountType
from searchv2.builders import rebuild_project_search_index

Expand Down Expand Up @@ -187,28 +186,29 @@ def publish_project(request, slug):
return HttpResponseForbidden("permission denied")



@login_required
def edit_images(request, slug, template_name="package/images_form.html"):
project = get_object_or_404(Project, slug=slug)
if not request.user.profile.can_edit_package(project):
return HttpResponseForbidden("permission denied")

if request.POST:
formset = ProjectImagesFormSet(data=request.POST, files=request.FILES, project=project,)
formset = ProjectImagesUrlFormSet(data=request.POST, project=project.id, user_data=request.user)
else:
formset = ProjectImagesFormSet(project=project, queryset=ProjectImage.objects.filter(project=project))
formset = ProjectImagesUrlFormSet(project=project.id, user_data=request.user)

if formset.is_valid():
formset.save()

for form in formset.forms:
form.save()
messages.add_message(request, messages.INFO, 'Project updated successfully')
return HttpResponseRedirect(reverse("package", kwargs={"slug": project.slug}))

return render(request, template_name, {
"formset": formset,
"images": ProjectImage.objects.filter(project=project),
"package": project,
"action": "Save",
"FILESTACK_API_KEY": settings.FILESTACK_API_KEY,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add it also to .env.example

})


Expand Down
2 changes: 2 additions & 0 deletions settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,3 +541,5 @@
CHRONIKER_CHECK_LOCK_FILE = False
CHRONIKER_DISABLE_RAW_COMMAND = True
CHRONIKER_EMAIL_SENDER = 'Chroniker'

FILESTACK_API_KEY = environ.get('FILESTACK_API_KEY')
Binary file added static/img/favicon_hiveprojects.ico
Binary file not shown.
File renamed without changes.
Binary file added static/img/logo_hiveprojects.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion static/scss/new/layout/_footer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@

&__item {
margin: 0 10px;
color: $base-footer-color;
}

&__link {
color: $base-font-color;
color: $base-footer-link-font-color;
}
}
Loading