diff --git a/.gitignore b/.gitignore index 9006ab5..e075ae2 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ static/ __pycache__/ env/ node_modules/ +themes/ +demo/ \ No newline at end of file diff --git a/assets/src/app/app.component.html b/assets/src/app/app.component.html index fa87238..e5fe93c 100644 --- a/assets/src/app/app.component.html +++ b/assets/src/app/app.component.html @@ -54,6 +54,12 @@

+
+ +
+
diff --git a/assets/src/app/app.component.ts b/assets/src/app/app.component.ts index 5ac9619..04ec300 100644 --- a/assets/src/app/app.component.ts +++ b/assets/src/app/app.component.ts @@ -11,10 +11,13 @@ import { Title } from '@angular/platform-browser'; styleUrls: ['./app.component.css'], providers: [AuthService] }) + export class AppComponent implements OnInit { usersForm; errors; rememberMe:boolean = false; + domain_url = '192.168.2.30'; + forgetPasswordUrl = "http://"+this.domain_url+":8000/user/password_reset/"; constructor( private authService: AuthService, diff --git a/assets/src/app/commons/services/auth/auth.service.ts b/assets/src/app/commons/services/auth/auth.service.ts index 792ca97..efa38be 100644 --- a/assets/src/app/commons/services/auth/auth.service.ts +++ b/assets/src/app/commons/services/auth/auth.service.ts @@ -8,13 +8,14 @@ export class AuthService { rememberMe:boolean; token; user; + domain_url = '192.168.2.30'; constructor(private http: HttpClient) { } // Generate token upon login loginAuth(user,remember){ this.rememberMe = remember; this.user = user; - return this.http.post("http://localhost:8000/user/login/", user) + return this.http.post("http://"+this.domain_url+":8000/user/login/", user) .toPromise() .then( response => { @@ -31,7 +32,7 @@ export class AuthService { // Generate token upon register registerAuth(user){ - return this.http.post("http://localhost:8000/user/register/", user) + return this.http.post("http://"+this.domain_url+"/user/register/", user) .toPromise() .then( response => { @@ -44,7 +45,7 @@ export class AuthService { } refreshToken(user){ - return this.http.get("http://localhost:8000/user/refresh/", user) + return this.http.get("http://"+this.domain_url+"localhost:8000/user/refresh/", user) .toPromise() .then( response => { diff --git a/assets/src/app/commons/services/cart/cart.service.ts b/assets/src/app/commons/services/cart/cart.service.ts index f202955..19f6b9a 100644 --- a/assets/src/app/commons/services/cart/cart.service.ts +++ b/assets/src/app/commons/services/cart/cart.service.ts @@ -7,11 +7,14 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; export class CartService { httpHeaders = new HttpHeaders({'Content-type':'application/json'}); + domain_url = '192.168.2.30'; + edit; + constructor( private http: HttpClient) { } getThemeCart(id){ - return this.http.get('http://localhost:8000/home/theme/cart/'+id+'/', {headers: this.httpHeaders}) + return this.http.get('http://'+this.domain_url+':8000/home/theme/cart/'+id+'/', {headers: this.httpHeaders}) .toPromise() .then( response => { @@ -24,4 +27,36 @@ export class CartService { } ) } + + buyThemeService(id){ + return this.http.get('http://'+this.domain_url+':8000/details/download/'+id+'/', {headers: this.httpHeaders}) + .toPromise() + .then( + response => { + return response; + } + ) + .catch( + error => { + return error; + } + ) + + } + + editLicenseService(id,license_id){ + this.edit = {'id': id, 'license_id': license_id} + return this.http.post('http://'+this.domain_url+':8000/home/theme/edit_license/', this.edit) + .toPromise() + .then( + response => { + return response; + } + ) + .catch( + error => { + return error; + } + ) + } } diff --git a/assets/src/app/commons/services/details/details.service.ts b/assets/src/app/commons/services/details/details.service.ts index 7762845..b2efe96 100644 --- a/assets/src/app/commons/services/details/details.service.ts +++ b/assets/src/app/commons/services/details/details.service.ts @@ -6,12 +6,13 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; }) export class DetailsService { + domain_url = '192.168.2.30'; httpHeaders = new HttpHeaders({'Content-type': 'application/json'}); constructor( private http: HttpClient) { } getThemeDetailsService(id){ - return this.http.get('http://localhost:8000/home/theme/details/'+id+'/', {headers: this.httpHeaders}) + return this.http.get('http://'+this.domain_url+':8000/home/theme/details/'+id+'/', {headers: this.httpHeaders}) .toPromise() .then( response =>{ @@ -26,7 +27,7 @@ export class DetailsService { } createReviewService(comment){ - return this.http.post("http://localhost:8000/details/createReview/",comment) + return this.http.post("http://"+this.domain_url+":8000/details/createReview/",comment) .toPromise() .then( response => { @@ -39,4 +40,19 @@ export class DetailsService { } ); } + + subscribeService(data){ + return this.http.post("http://"+this.domain_url+":8000/home/theme/subscribe/",data) + .toPromise() + .then( + response => { + return response; + } + ) + .catch( + error => { + return error; + } + ) + } } diff --git a/assets/src/app/commons/services/home/home.service.ts b/assets/src/app/commons/services/home/home.service.ts index d9b62c2..a15edb6 100644 --- a/assets/src/app/commons/services/home/home.service.ts +++ b/assets/src/app/commons/services/home/home.service.ts @@ -7,6 +7,7 @@ import { Observable } from 'rxjs'; }) export class HomeService { + domain_url = '192.168.2.30'; httpHeaders = new HttpHeaders({'Content-type': 'application/json'}); public categories; constructor(private http: HttpClient) { @@ -14,7 +15,7 @@ export class HomeService { } getThemes(){ - return this.http.get("http://localhost:8000/home/theme/", {headers: this.httpHeaders}) + return this.http.get("http://"+this.domain_url+":8000/home/theme/", {headers: this.httpHeaders}) .toPromise() .then( response => { @@ -29,7 +30,23 @@ export class HomeService { } getCategory(){ - return this.http.get("http://localhost:8000/home/theme/category/", {headers: this.httpHeaders}) + return this.http.get("http://"+this.domain_url+":8000/home/theme/category/", {headers: this.httpHeaders}) + .toPromise() + .then( + response => { + return response; + } + ) + .catch( + error => { + return error; + } + ) + } + + subscribeService(data){ + console.log('clicked'); + return this.http.post("http://"+this.domain_url+":8000/home/theme/subscribe/", data) .toPromise() .then( response => { diff --git a/assets/src/app/components/cart/cart.component.html b/assets/src/app/components/cart/cart.component.html index e398c47..2209e5e 100644 --- a/assets/src/app/components/cart/cart.component.html +++ b/assets/src/app/components/cart/cart.component.html @@ -2,7 +2,7 @@
- +
@@ -12,7 +12,7 @@

{{theme.name}}

License Type:

{{ theme.license.license }}

- Change + Change

Unit Price:

@@ -28,15 +28,45 @@

${{ theme.price }}

Payment:

-
+
- + + +
- \ No newline at end of file + + + diff --git a/assets/src/app/components/cart/cart.component.ts b/assets/src/app/components/cart/cart.component.ts index 735e6cd..32eb596 100644 --- a/assets/src/app/components/cart/cart.component.ts +++ b/assets/src/app/components/cart/cart.component.ts @@ -13,6 +13,9 @@ export class CartComponent implements OnInit { theme; discount; dis_price; + domain_url = '192.168.2.30'; + category; + constructor( private cartService: CartService, private route: ActivatedRoute, @@ -51,6 +54,36 @@ export class CartComponent implements OnInit { ) } + buyTheme(event,theme_id){ + console.log('clicked'); + this.cartService.buyThemeService(theme_id) + .then( + response => { + return response; + } + ) + .catch( + error => { + return error; + } + ) + } + + changeLicense(event,theme_id,license_id){ + this.cartService.editLicenseService(theme_id,license_id) + .then( + response => { + this.themeCart(); + return response; + } + ) + .catch( + error => { + return error; + } + ) + } + } diff --git a/assets/src/app/components/details/details.component.html b/assets/src/app/components/details/details.component.html index bcd60f4..e6d9bbc 100644 --- a/assets/src/app/components/details/details.component.html +++ b/assets/src/app/components/details/details.component.html @@ -22,7 +22,7 @@
{{ theme.name }}
- +
@@ -108,7 +108,7 @@

{{ theme.name }}

Screenshots

- +
@@ -342,12 +342,17 @@

Reviews

Be the first to know!

Get the updates about new products.

+ +

{{ message }}

+
+
+
\ No newline at end of file diff --git a/assets/src/app/components/details/details.component.ts b/assets/src/app/components/details/details.component.ts index 6565c90..93377c4 100644 --- a/assets/src/app/components/details/details.component.ts +++ b/assets/src/app/components/details/details.component.ts @@ -1,6 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { DetailsService } from '../../commons/services/details/details.service'; + import { Title } from '@angular/platform-browser'; import { FormBuilder, FormGroup, FormControl, Validators } from '@angular/forms'; @@ -19,6 +20,9 @@ export class DetailsComponent implements OnInit { reviews; content; token; + subscribe; + message; + domain_url = '192.168.2.30'; constructor( private route: ActivatedRoute, @@ -40,6 +44,10 @@ export class DetailsComponent implements OnInit { rating : new FormControl('') }); + this.subscribe = this.fb.group({ + email : new FormControl('', Validators.required), + }); + } get review(){ @@ -50,6 +58,10 @@ export class DetailsComponent implements OnInit { return this.review.get('rating'); } + get email(){ + return this.subscribe.get('email'); + } + // get details of the theme getThemeDetails(){ this.detailsService.getThemeDetailsService(this.theme_id) @@ -90,6 +102,7 @@ export class DetailsComponent implements OnInit { this.getThemeDetails(); this.review.reset(); this.currentRate =0 + return response; } ) .catch( @@ -99,6 +112,20 @@ export class DetailsComponent implements OnInit { ) } + // subscribe user (details page) + subscribeMarket(){ + this.detailsService.subscribeService(this.subscribe.value) + .then( + response => { + this.message = response.message; + return response; + } + ) + .catch( + error => { + return error; + } + ) + } - } diff --git a/assets/src/app/components/home/home.component.html b/assets/src/app/components/home/home.component.html index 0310313..7961365 100644 --- a/assets/src/app/components/home/home.component.html +++ b/assets/src/app/components/home/home.component.html @@ -136,12 +136,18 @@
{{ theme.name }}

Be the first to know!

Get the updates about new products.

+ +

{{ message }}

+
+
\ No newline at end of file diff --git a/assets/src/app/components/home/home.component.ts b/assets/src/app/components/home/home.component.ts index 222f831..14c9dff 100644 --- a/assets/src/app/components/home/home.component.ts +++ b/assets/src/app/components/home/home.component.ts @@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { HomeService } from '../../commons/services/home/home.service'; import { Title } from '@angular/platform-browser'; import { CategoryPipe } from '../../commons/pipes/category/category.pipe'; -import { FormBuilder, FormGroup } from '@angular/forms'; +import { FormBuilder, FormGroup, FormControl, Validators } from '@angular/forms'; const categories = ['Angular JS','E-Commerce','General','Bootstrap 4']; @@ -17,16 +17,27 @@ export class HomeComponent implements OnInit { themes; category; searchCategory; - baseUrl = "http://localhost:8000/media/"; - + domain_url = '192.168.2.30'; + baseUrl = "http://"+this.domain_url+":8000/media/"; + subscriber; + message; constructor( private home: HomeService, - private title: Title) {} + private title: Title, + private fb: FormBuilder) {} ngOnInit() { this.getThemesHome(); this.title.setTitle('Home - Marketplace'); + this.subscriber = this.fb.group({ + email : new FormControl('', Validators.required) + }); + + } + + get email(){ + return this.subscriber.email; } getThemesHome(){ @@ -58,4 +69,20 @@ export class HomeComponent implements OnInit { return `${choice}`; } + subscribeMarket(){ + console.log('clicked'); + this.home.subscribeService(this.subscriber.value) + .then( + response => { + this.message = response.message; + return response; + } + ) + .catch( + error => { + return error; + } + ) + } + } diff --git a/details/urls.py b/details/urls.py index 5be5e0b..cb93a3d 100644 --- a/details/urls.py +++ b/details/urls.py @@ -3,4 +3,5 @@ urlpatterns = [ path('createReview/', views.CreateReview.as_view()), + path('download//',views.DownloadTheme.as_view()), ] \ No newline at end of file diff --git a/details/views.py b/details/views.py index cf8415e..f9cee80 100644 --- a/details/views.py +++ b/details/views.py @@ -6,6 +6,11 @@ from rest_framework.response import Response from .serializers import ReviewSerializer from rest_framework.authtoken.models import Token +from django.views.generic import View +from io import StringIO +from zipfile import ZipFile +from django.conf import settings +import os @@ -53,6 +58,19 @@ def get_average_rating(self,list_values,key): return self.sum_values/len(list_values) +class DownloadTheme(APIView): + """download theme view + """ + permission_classes = (AllowAny,) + + def get(self,*args,**kwargs): + theme = Theme.objects.get(id=kwargs.get('theme_id')) + file = str(theme.file) + file.replace(" ","%20") + return Response({'download': file}) + + + diff --git a/market/settings.py b/market/settings.py index 1b91af0..6ad73c8 100644 --- a/market/settings.py +++ b/market/settings.py @@ -9,7 +9,7 @@ # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = [] +ALLOWED_HOSTS = ["*"] # Application definition @@ -159,6 +159,15 @@ os.path.join(BASE_DIR, 'assets/'), ] +EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' +EMAIL_HOST = 'smtp.gmail.com' +EMAIL_PORT = 587 +EMAIL_HOST_USER = 'bambolino35@gmail.com' +EMAIL_HOST_PASSWORD = 'fqu8moex' +DEFAULT_FROM_EMAIL= EMAIL_HOST_USER +EMAIL_USE_TLS = True +EMAIL_USE_SSL = False + # Allow any settings to be defined in local_settings.py which should be # ignored in your version control system allowing for settings to be # defined per machine. diff --git a/templates/base.html b/templates/base.html index e69de29..88bc7f0 100644 --- a/templates/base.html +++ b/templates/base.html @@ -0,0 +1,52 @@ +{% load static %} + + + + + + + + + + + + + + + + + + + {% block title %} + Marketplace + {% endblock title %} + + + + +
+ {% block content %} + + {% endblock content %} +
+ + + + + + + + + + \ No newline at end of file diff --git a/templates/registration/password_reset_complete.html b/templates/registration/password_reset_complete.html new file mode 100644 index 0000000..11aa7c5 --- /dev/null +++ b/templates/registration/password_reset_complete.html @@ -0,0 +1,13 @@ +{% extends 'base.html' %} +{% load il8n %} + +{% block content %} +
+
+

Forget your password?

+

+ {% trans "Your password has been set. Return to homepage to log " %} +

+
+
+{% endblock content %} \ No newline at end of file diff --git a/templates/registration/password_reset_confirm.html b/templates/registration/password_reset_confirm.html new file mode 100644 index 0000000..a589a25 --- /dev/null +++ b/templates/registration/password_reset_confirm.html @@ -0,0 +1,46 @@ +{% extends 'base.html' %} +{% load widget_tweaks %} +{% load i18n %} + +{% block content %} +{% if validlink %} +
+
+

Forget your password?

+

+ {% trans "Please enter your new password twice so we can verify you typed it in correctly." %} +

+
+
+
+
+ {% csrf_token %} +
+
+ + {{ form.new_password1.errors }} + + + {{ form.new_password1|add_class:"form-control"|attr:"type:text"|attr:"placeholder:New password" }} +
+
+ + {{ form.new_password2.errors }} + + + {{ form.new_password2|add_class:"form-control"|attr:"type:text"|attr:"placeholder:Confirm password"}} +
+ + +
+
+
+
+
+ +{% else%} +

{% trans "The password reset link was invalid, possibly because it has already been used. Please request a new password reset." %}

+ +{% endif %} + +{% endblock %} \ No newline at end of file diff --git a/templates/registration/password_reset_done.html b/templates/registration/password_reset_done.html new file mode 100644 index 0000000..57a9276 --- /dev/null +++ b/templates/registration/password_reset_done.html @@ -0,0 +1,13 @@ +{% extends 'base.html' %} +{% load i18n %} + +{% block content %} +
+

{% trans "We've emailed you instructions for setting your password, if an account exists with the email you entered. You should receive them shortly." %}

+ +

{% trans "If you don't receive an email, please make sure you've entered the address you registered with, and check your spam folder." %}

+
+ Go to Homepage + +
+{% endblock content %} \ No newline at end of file diff --git a/templates/registration/password_reset_email.html b/templates/registration/password_reset_email.html new file mode 100644 index 0000000..2a55eee --- /dev/null +++ b/templates/registration/password_reset_email.html @@ -0,0 +1,12 @@ +{% autoescape off %} +To initiate the password reset process for your {{ user.get_username }} Swiftkind Market Account, +click the link below: + +{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %} + +If clicking the link above doesn't work, please copy and paste the URL in a new browser +window instead. + +Sincerely, +The Swiftkind Team +{% endautoescape %} \ No newline at end of file diff --git a/templates/registration/password_reset_form.html b/templates/registration/password_reset_form.html new file mode 100644 index 0000000..4028578 --- /dev/null +++ b/templates/registration/password_reset_form.html @@ -0,0 +1,35 @@ +{% extends 'base.html' %} +{% load widget_tweaks %} + +{% block title %} +Forget Password - Marketplace +{% endblock title %} + +{% block content %} +
+
+

Forget your password?

+

+ Please enter your email address, we will respond with instructions to reset your password. +

+
+
+
+ {% csrf_token %} +
+
+ + {{ form.email|add_class:"form-control"|attr:"placeholder:example@example.com" }} + + {{ form.email.errors }} + + +
+ +
+ +
+
+
+ +{% endblock content %} \ No newline at end of file diff --git a/templates/registration/password_reset_subject.txt b/templates/registration/password_reset_subject.txt new file mode 100644 index 0000000..45a354b --- /dev/null +++ b/templates/registration/password_reset_subject.txt @@ -0,0 +1,3 @@ +{% load i18n %}{% autoescape off %} +{% blocktrans %}Password reset on {{ site_name }}{% endblocktrans %} +{% endautoescape %} \ No newline at end of file diff --git a/themes/admin.py b/themes/admin.py index 4275afc..bfd90ed 100644 --- a/themes/admin.py +++ b/themes/admin.py @@ -1,136 +1,149 @@ from django.contrib import admin from django.contrib.auth.admin import UserAdmin -from .models import ( UserDownloadLog, Theme, Review, Thumbnail, Screenshot, Browser, Category, Topic, Label, License) +from .models import ( + UserDownloadLog, Theme, Review, Thumbnail, Screenshot, Browser, Category, Topic, Label, License, Subscriber) class UserDownloadLogAdmin(admin.ModelAdmin): - """user download log admin - """ - model = UserDownloadLog + """user download log admin + """ + model = UserDownloadLog - list_display = ( - 'user', - 'theme', - 'download_times', - 'date_created', - 'date_modified', - ) + list_display = ( + 'user', + 'theme', + 'download_times', + 'date_created', + 'date_modified', + ) class ThemeAdmin(admin.ModelAdmin): - """themes admin - """ - model = Theme + """themes admin + """ + model = Theme - list_display = ( - 'name', - 'description', - 'rating', - 'price', - 'discount', - 'version', - ) + list_display = ( + 'name', + 'description', + 'rating', + 'price', + 'discount', + 'version', + ) class ReviewAdmin(admin.ModelAdmin): - """review admin - """ - model = Review + """review admin + """ + model = Review - list_display = ( - 'user', - 'rating', - 'comment', - 'date_created', - 'date_modified', - ) + list_display = ( + 'user', + 'rating', + 'comment', + 'date_created', + 'date_modified', + ) class ThumbnailAdmin(admin.ModelAdmin): - """thumbnail admin - """ - model = Thumbnail + """thumbnail admin + """ + model = Thumbnail - list_display = ( - 'theme', - 'thumbnail', - 'date_created', - 'date_modified', - ) + list_display = ( + 'theme', + 'thumbnail', + 'date_created', + 'date_modified', + ) class ScreenshotAdmin(admin.ModelAdmin): - """screenshot admin - """ - model = Screenshot + """screenshot admin + """ + model = Screenshot - list_display = ( - 'theme', - 'image', - 'date_created', - 'date_modified', - ) + list_display = ( + 'theme', + 'image', + 'date_created', + 'date_modified', + ) class BrowserAdmin(admin.ModelAdmin): - """browsers admin - """ - model = Browser + """browsers admin + """ + model = Browser - list_display = ( - 'browser', - 'date_created', - 'date_modified', - ) + list_display = ( + 'browser', + 'date_created', + 'date_modified', + ) class CategoryAdmin(admin.ModelAdmin): - """category admin - """ - model = Category + """category admin + """ + model = Category - list_display = ( - 'category', - 'date_created', - 'date_modified', - ) + list_display = ( + 'category', + 'date_created', + 'date_modified', + ) class TopicAdmin(admin.ModelAdmin): - """topic admin - """ - model = Topic + """topic admin + """ + model = Topic - list_display = ( - 'topic', - 'date_created', - 'date_modified', - ) + list_display = ( + 'topic', + 'date_created', + 'date_modified', + ) class LabelAdmin(admin.ModelAdmin): - """label admin - """ - model = Label + """label admin + """ + model = Label - list_display = ( - 'label', - 'date_created', - 'date_modified', - ) + list_display = ( + 'label', + 'date_created', + 'date_modified', + ) class LicenseAdmin(admin.ModelAdmin): - """license admin - """ - model = License + """license admin + """ + model = License - list_display = ( - 'license', - 'date_created', - 'date_modified', - ) + list_display = ( + 'license', + 'date_created', + 'date_modified', + ) +class SubscriberAdmin(admin.ModelAdmin): + """subscribers admin + """ + + model = Subscriber + + list_display = ( + 'user', + 'date_created', + 'date_modified', + ) + admin.site.register(UserDownloadLog, UserDownloadLogAdmin) admin.site.register(Theme, ThemeAdmin) @@ -142,3 +155,4 @@ class LicenseAdmin(admin.ModelAdmin): admin.site.register(Topic, TopicAdmin) admin.site.register(Label, LabelAdmin) admin.site.register(License, LicenseAdmin) +admin.site.register(Subscriber, SubscriberAdmin) diff --git a/themes/models.py b/themes/models.py index e308705..8c208ae 100644 --- a/themes/models.py +++ b/themes/models.py @@ -9,6 +9,9 @@ def thumbnail_upload_path(instance, filename): def screenshot_upload_path(instance, filename): return f'images/{instance.theme.id}/screenshot/{filename}' +def theme_file_upload_path(instance, filename): + return f'download/{instance.id}/{instance.name}/{filename}' + class UserDownloadLog(models.Model): """user download log @@ -49,12 +52,13 @@ class Theme(models.Model): topic = models.ForeignKey('themes.Topic', on_delete=models.CASCADE, blank=True) labels = models.ManyToManyField('themes.Label', blank=True) license = models.ForeignKey('themes.License', on_delete=models.CASCADE, blank=True, null=True) + file = models.FileField(upload_to=theme_file_upload_path, null=True) release_date = models.DateField(auto_now=False,auto_now_add=False, blank=True) date_modified = models.DateField(auto_now=True) def __str__(self): - return f'{self.name, self.price, self.rating, self.version,}' + return f'{self.name, self.price, self.rating, self.version, self.file, self.license}' class Review(models.Model): @@ -158,5 +162,17 @@ def __str__(self): return f'{self.license,}' +class Subscriber(models.Model): + """subscribe + """ + user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) + + date_created = models.DateField(auto_now_add=True) + date_modified = models.DateField(auto_now=True) + + def __str__(self): + return f'{self.user.email}' + + diff --git a/themes/urls.py b/themes/urls.py index 1873102..ac3bc06 100644 --- a/themes/urls.py +++ b/themes/urls.py @@ -6,4 +6,6 @@ path('theme/details//', views.ThemeNameFilter.as_view()), path('theme/cart//', views.ThemeCart.as_view()), path('theme/category/',views.CategoryView.as_view()), + path('theme/edit_license/', views.EditLicense.as_view()), + path('theme/subscribe/', views.Subscribe.as_view()), ] \ No newline at end of file diff --git a/themes/views.py b/themes/views.py index 42e09b5..199b3c6 100644 --- a/themes/views.py +++ b/themes/views.py @@ -1,11 +1,14 @@ from django.shortcuts import render +from django.conf import settings from django.core import serializers -from rest_framework.views import APIView -from .models import (Theme, Thumbnail, Screenshot, Review, Browser, Category, Topic, Label, License) +from django.views.generic import View +from .models import (Theme, Thumbnail, Screenshot, Review, Browser, Category, Topic, Label, License, Subscriber) +from users.models import User from .serializers import (ThemeDetailSerializer, ThumbnailSerializer, CategorySerializer, TopicSerializer, LicenseSerializer) +from rest_framework.views import APIView from rest_framework.permissions import AllowAny from rest_framework.response import Response - +from django.core.mail import send_mail class ThemeFeed(APIView): """themes home @@ -93,12 +96,14 @@ def get(self,*args,**kwargs): category = Category.objects.get(id=theme.category_id) thumbnail = Thumbnail.objects.get(theme_id=theme.id) license = License.objects.get(id=theme.license_id) + licenses = License.objects.all().values('pk','license') theme_s = ThemeDetailSerializer(theme).data theme_s['thumbnail'] = ThumbnailSerializer(thumbnail).data theme_s['category'] = CategorySerializer(category).data theme_s['license'] = LicenseSerializer(license).data - + theme_s['licenses'] = {'license': list(licenses)} + return Response(theme_s, status=200) @@ -111,3 +116,54 @@ def get(self,*args,**kwargs): category = Category.objects.all().values('category') return Response({'category': list(category)}, status=200) + + +class EditLicense(APIView): + """change license type + """ + permission_classes = (AllowAny,) + + def post(self,request,*args,**kwargs): + theme = Theme.objects.get(id=request.data['id']) + license = License.objects.get(id=request.data['license_id']) + theme.license = license + theme.save() + return Response({'success': 'license changed'},status=200) + + +class Subscribe(APIView): + """send email for users to be updated with the latest published themes + """ + permission_classes = (AllowAny,) + + def post(self,request,*args,**kwargs): + + """check if user is registered to the marketplace + """ + try: + user = User.objects.get(email=request.data['email']) + except: + return Response({'message': 'Please register before subscribing to the market'}) + + """check if subscriber is already subscribed to the marketplace + """ + subscriber = Subscriber.objects.filter(user=user) + if subscriber.exists(): + return Response({'message': 'You are already subscribed!'}, status=200) + + """add user as a subscriber + """ + subscribe = Subscriber.objects.create(user=user) + + """send email via Gmail only + """ + send_mail('Subscribed user', + 'Thank you for subscribing on our theme market, we will send you emails for the latest templates', + settings.EMAIL_HOST_USER, + [subscribe.user.email], + fail_silently=False, + ) + + return Response({'message': 'You are now subscribed!'}, status=200) + + diff --git a/users/urls.py b/users/urls.py index 3b1f842..f3742b5 100644 --- a/users/urls.py +++ b/users/urls.py @@ -1,4 +1,5 @@ -from django.urls import path, include +from django.urls import path, include, re_path +from django.contrib.auth import views as auth_views from . import views from rest_framework import routers @@ -7,4 +8,11 @@ path('register/', views.Register.as_view()), path('refresh/',views.RefreshToken.as_view()), path('auth/', include('rest_framework.urls', namespace='rest_framework')), + + # forget password links + path('password_reset/', auth_views.PasswordResetView.as_view(), name='password_reset'), + path('password_reset/done/', auth_views.PasswordResetDoneView.as_view(), name='password_reset_done'), + re_path(r'^reset/(?P[0-9A-Za-z_\-]+)/(?P[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', + auth_views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'), + path('reset/done/', auth_views.PasswordResetCompleteView.as_view(), name='password_reset_complete'), ] diff --git a/users/views.py b/users/views.py index d236539..c3cf6ab 100644 --- a/users/views.py +++ b/users/views.py @@ -6,6 +6,7 @@ from .serializers import LoginSerializer, RegisterSerializer from .managers import UserManager from rest_framework.exceptions import ValidationError +from django.contrib.auth.views import PasswordResetView from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import parsers, renderers