Changes added documents functionality, displaying preview icon for documents, downloading documents files (#152)

* removed migartion files, created single migration file, updating closed on, start & end date for emails, events, cases, opportunities,

* added documents functionality, displaying preview icon for documents, downloading documents
This commit is contained in:
nikhila05 2019-01-28 20:55:58 +05:30 committed by GitHub
parent 0cc376e240
commit 847272ce32
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 564 additions and 22 deletions

View file

@ -63,23 +63,6 @@ This project contains the following modules.
* Planner
Installation - Requirements
===========================
Ubuntu 64bit - 16.04
--------------------
$ sudo apt-get update && apt-get upgrade -y
$ sudo apt-get install -y curl wget libpq-dev python3-dev gem ruby ruby-dev build-essential libssl-dev libffi-dev python-dev python-virtualenv python-pip git redis-server libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev libharfbuzz-dev libfribidi-dev tcl8.6-dev tk8.6-dev python-tk
$ sudo gem install sass
If you cloned the package from git use virtualenv to install requirements::
pip install -r requirements.txt
Try
===
Demo Available `here`_.

View file

@ -1,7 +1,7 @@
from django import forms
from django.contrib.auth import authenticate
from django.contrib.auth.forms import PasswordResetForm
from common.models import Address, User
from common.models import Address, User, Document
class BillingAddressForm(forms.ModelForm):
@ -143,3 +143,10 @@ class PasswordResetEmailForm(PasswordResetForm):
if not User.objects.filter(email__iexact=email, is_active=True).exists():
raise forms.ValidationError("User doesn't exist with this Email")
return email
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
fields = ['title', 'document_file']

View file

@ -0,0 +1,26 @@
# Generated by Django 2.1.5 on 2019-01-28 07:28
import common.models
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('common', '0002_auto_20190128_1237'),
]
operations = [
migrations.CreateModel(
name='Document',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(blank=True, max_length=1000, null=True)),
('document_file', models.FileField(max_length=5000, upload_to=common.models.document_path)),
('created_on', models.DateTimeField(auto_now_add=True)),
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='document_uploaded', to=settings.AUTH_USER_MODEL)),
],
),
]

View file

@ -1,4 +1,6 @@
from django.db import models
from datetime import datetime
from django.template.defaultfilters import slugify
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, UserManager
@ -125,3 +127,149 @@ class Attachments(models.Model):
account = models.ForeignKey('accounts.Account', null=True, blank=True, related_name='account_attachment', on_delete=models.CASCADE)
contact = models.ForeignKey('contacts.Contact', on_delete=models.CASCADE, related_name='contact_attachment', blank=True, null=True)
opportunity = models.ForeignKey('opportunity.Opportunity',blank=True,null=True,on_delete=models.CASCADE,related_name='opportunity_attachment')
def document_path(self, filename):
hash_ = int(time.time())
return "%s/%s/%s" % ("docs", hash_, filename)
class Document(models.Model):
title = models.CharField(max_length=1000, blank=True, null=True)
document_file = models.FileField(upload_to=document_path, max_length=5000)
created_by = models.ForeignKey(User, related_name='document_uploaded', on_delete=models.CASCADE)
created_on = models.DateTimeField(auto_now_add=True)
def is_document_file_image(self, ext):
image_ext_list = ['bmp', 'dds', 'gif', 'jpg', 'jpeg', 'png', 'psd', 'pspimage',
'tga', 'thm', 'tif', 'tiff', 'yuv']
return ext.lower() in image_ext_list
def is_document_file_audio(self, ext):
audio_ext_list = ['aif', 'iff', 'm3u', 'm4a', 'mid', 'mp3',
'mpa', 'wav', 'wma']
return ext.lower() in audio_ext_list
def is_document_file_video(self, ext):
video_ext_list = ['3g2', '3gp', 'asf', 'avi', 'flv', 'm4v', 'mov',
'mp4', 'mpg', 'rm', 'srt', 'swf', 'vob', 'wmv']
return ext.lower() in video_ext_list
def is_document_file_pdf(self, ext):
pdf_ext_list = ['indd', 'pct', 'pdf']
return ext.lower() in pdf_ext_list
def is_document_file_code(self, ext):
code_ext_list = ['aspx', 'json', 'jsp', 'do', 'htm', 'html', 'ser',
'php', 'jad', 'cfm', 'xml', 'js', 'pod', 'asp',
'atomsvc', 'rdf', 'pou', 'jsf', 'abs', 'pl', 'asm',
'srz', 'luac', 'cod', 'lib', 'arxml', 'bas', 'ejs',
'fs', 'hbs', 's', 'ss', 'cms', 'pyc', 'vcxproj',
'jse', 'smali', 'xla', 'lxk', 'pdb', 'src', 'cs',
'ipb', 'ave', 'mst', 'vls', 'rcc', 'sax', 'scr',
'dtd', 'axd', 'mrl', 'xsl', 'ino', 'spr', 'xsd',
'cgi', 'isa', 'ws', 'rss', 'dvb', 'nupkg', 'xlm',
'v4e', 'rss', 'prg', 'form', 'bat', 'mrc', 'asi',
'jdp', 'fmb', 'graphml', 'gcode', 'aia', 'py', 'atp',
'mzp', 'o', 'scs', 'mm', 'cpp', 'java', 'gypi', 'idb',
'txml', 'c', 'vip', 'tra', 'rc', 'action', 'vlx',
'asta', 'pyo', 'lua', 'gml', 'prl', 'rfs', 'cpb',
'sh', 'rbf', 'gp', 'phtml', 'bp', 'scb', 'sln', 'vbp',
'wbf', 'bdt', 'mac', 'rpy', 'eaf', 'mc', 'mwp', 'gnt',
'h', 'swift', 'e', 'styl', 'cxx', 'as', 'liquid',
'dep', 'fas', 'vbs', 'aps', 'vbe', 'lss', 'cmake',
'resx', 'csb', 'dpk', 'pdml', 'txx', 'dbg', 'jsa',
'sxs', 'sasf', 'pm', 'csx', 'r', 'wml', 'au3', 'stm',
'cls', 'cc', 'ins', 'jsc', 'dwp', 'rpg', 'arb', 'bml',
'inc', 'eld', 'sct', 'sm', 'wbt', 'csproj', 'tcz',
'html5', 'gbl', 'cmd', 'dlg', 'tpl', 'rbt', 'xcp',
'tpm', 'qry', 'mfa', 'ptx', 'lsp', 'pag', 'ebc',
'php3', 'cob', 'csc', 'pyt', 'dwt', 'rb', 'wsdl',
'lap', 'textile', 'sfx', 'x', 'a5r', 'dbp', 'pmp',
'ipr', 'fwx', 'pbl', 'vbw', 'phl', 'cbl', 'pas',
'mom', 'dbmdl', 'lol', 'wdl', 'ppam', 'plx', 'vb',
'cgx', 'lst', 'lmp', 'vd', 'bcp', 'thtml', 'scpt',
'isu', 'mrd', 'perl', 'dtx', 'f', 'wpk', 'ipf', 'ptl',
'luca', 'hx', 'uvproj', 'qvs', 'vba', 'xjb',
'appxupload', 'ti', 'svn-base', 'bsc', 'mak',
'vcproj', 'dsd', 'ksh', 'pyw', 'bxml', 'mo', 'irc',
'gcl', 'dbml', 'mlv', 'wsf', 'tcl', 'dqy', 'ssi',
'pbxproj', 'bal', 'trt', 'sal', 'hkp', 'vbi', 'dob',
'htc', 'p', 'ats', 'seam', 'loc', 'pli', 'rptproj',
'pxml', 'pkb', 'dpr', 'scss', 'dsb', 'bb', 'vbproj',
'ash', 'rml', 'nbk', 'nvi', 'lmv', 'mw', 'jl', 'dso',
'cba', 'jks', 'ary', 'run', 'vps', 'clm', 'brml',
'msha', 'mdp', 'tmh', 'rdf', 'jsx', 'sdl', 'ptxml',
'fxl', 'wmw', 'dcr', 'bcc', 'cbp', 'bmo', 'bsv',
'less', 'gss', 'ctl', 'rpyc', 'ascx', 'odc', 'wiki',
'obr', 'l', 'axs', 'bpr', 'ppa', 'rpo', 'sqlproj',
'smm', 'dsr', 'arq', 'din', 'jml', 'jsonp', 'ml',
'rc2', 'myapp', 'cla', 'xme', 'obj', 'jsdtscope',
'gyp', 'datasource', 'cp', 'rh', 'lpx', 'a2w', 'ctp',
'ulp', 'nt', 'script', 'bxl', 'gs', 'xslt', 'mg',
'pch', 'mhl', 'zpd', 'psm1', 'asz', 'm', 'jacl',
'pym', 'rws', 'acu', 'ssq', 'wxs', 'coffee', 'ncb',
'akt', 'pyx', 'zero', 'hs', 'mkb', 'tru', 'xul',
'mfl', 'sca', 'sbr', 'master', 'opv', 'matlab',
'sami', 'agc', 'slim', 'tea', 'pbl', 'm51', 'mec',
'asc', 'gch', 'enml', 'ino', 'kst', 'jade', 'dfb',
'ips', 'rgs', 'vbx', 'cspkg', 'ncx', 'brs', 'wfs',
'ifp', 'nse', 'xtx', 'j', 'cx', 'ps1', 'nas', 'mk',
'ccs', 'vrp', 'lnp', 'cml', 'c#', 'idl', 'exp', 'apb',
'nsi', 'asmx', 'tdo', 'pjt', 'fdt', 's5d', 'mvba',
'mf', 'odl', 'bzs', 'jardesc', 'tgml', 'moc', 'wxi',
'cpz', 'fsx', 'jav', 'ocb', 'agi', 'tec', 'txl',
'amw', 'mscr', 'dfd', 'dpd', 'pun', 'f95', 'vdproj',
'xsc', 'diff', 'wxl', 'dgml', 'airi', 'kmt', 'ksc',
'io', 'rbw', 'sas', 'vcp', 'resources', 'param',
'cg', 'hlsl', 'vssscc', 'bgm', 'xn', 'targets', 'sl',
'gsc', 'qs', 'owl', 'devpak', 'phps', 'hdf', 'pri',
'nbin', 'xaml', 's4e', 'scm', 'tk', 'poc', 'uix',
'clw', 'factorypath', 's43', 'awd', 'htr', 'php2',
'classpath', 'pickle', 'rob', 'msil', 'ebx', 'tsq',
'lml', 'f90', 'lds', 'vup', 'pbi', 'swt', 'vap', 'ig',
'pdo', 'frt', 'fcg', 'c++', 'xcl', 'dfn', 'aar',
'for', 're', 'twig', 'ebm', 'dhtml', 'hc', 'pro',
'ahk', 'rule', 'bsh', 'jcs', 'zrx', 'wsdd', 'csp',
'drc', 'appxsym']
return ext.lower() in code_ext_list
def is_document_file_text(self, ext):
text_ext_list = ['doc', 'docx', 'log', 'msg', 'odt', 'pages', 'rtf',
'tex', 'txt', 'wpd', 'wps']
return ext.lower() in text_ext_list
def is_document_file_sheet(self, ext):
sheet_ext_list = ['csv', 'xls', 'xlsx',
'xlsm', 'xlsb', 'xltx', 'xltm', 'xlt']
return ext.lower() in sheet_ext_list
def is_document_file_zip(self, ext):
ext_list = ['zip', '7Z', 'gz', 'rar', 'ZIPX', 'ACE', 'tar', ]
return ext.lower() in ext_list
def file_type(self):
name_ext_list = self.document_file.url.split(".")
if (len(name_ext_list) > 1):
ext = name_ext_list[int(len(name_ext_list) - 1)]
if self.is_document_file_audio(ext):
return ("audio", "fa fa-file-audio")
elif self.is_document_file_video(ext):
return ("video", "fa fa-file-video")
elif self.is_document_file_image(ext):
return ("image", "fa fa-file-image")
elif self.is_document_file_pdf(ext):
return ("pdf", "fa fa-file-pdf")
elif self.is_document_file_code(ext):
return ("code", "fa fa-file-code")
elif self.is_document_file_text(ext):
return ("text", "fa fa-file-alt")
elif self.is_document_file_sheet(ext):
return ("sheet", "fa fa-file-excel")
elif self.is_document_file_zip(ext):
return ("zip", "fa fa-file-archive")
return ("file", "fa fa-file")
return ("file", "fa fa-file")
def __str__(self):
return self.title

View file

@ -0,0 +1,72 @@
{% extends 'base.html' %}
{% load staticfiles %}
{% block breadcrumb %}
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'common:doc_list' %}">Documents</a></li>
<li class="breadcrumb-item">{% if doc_obj %}Edit{% else %}Create{% endif %}</li>
</ol>
</nav>
{% endblock breadcrumb %}
{% block content %}
<form id="add_form" method="POST" enctype="multipart/form-data" action="" novalidate>
<div class="overview_form_block row marl justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-body">
<div class="card-title text-center">
{% if doc_obj %}EDIT{% else %}CREATE{% endif %} DOCUMENT
</div>
<div class="row marl">
<div class="col-md-12">
<div class="filter_col col-md-12">
<div class="form-group">
<label for="id_firstname">Title {% if doc_form.title.field.required %}<span class="error">*</span>{% endif %}</label>
<input type="text" class="form-control" name="title" value="{% if doc_obj %}{{ doc_obj.title }}{% else %}{{request.POST.title}}{% endif %}" placeholder="Document title">
</div>
<span class="error" id="id_title">{{ errors.title }}</span>
</div>
<div class="filter_col col-md-12">
<div class="form-group">
<label for="exampleInputEmail1">Upload Document{% if doc_form.document_file.field.required %}<span class="error">*</span>{% endif %}</label>
<input type="file" name="document_file" class="form-control" />
</div>
{% if doc_obj %}<span>{{doc_obj.document_file }}</span>{% endif %}
<span class="error" id="id_document_file">{{ errors.document_file }}</span>
</div>
</div>
</div>
<div class="row marl buttons_row form_btn_row text-center">
<button class="btn btn-default save" type="submit">Save</button>
<a href="{% url 'common:doc_list' %}" class="btn btn-default clear" id="create_doc_cancel">Cancel</a>
</div>
</div>
</div>
</div>
</div>
</form>
{% endblock content %}
{% block js_block %}
<script src="https://malsup.github.io/jquery.form.js"></script>
<script type="text/javascript">
$('form#add_form').ajaxForm({
type:'POST',
dataType:'json',
url: ".",
data:$('#add_form').serialize(),
success: function(data) {
if(data.error){
$('.error').html('')
for (var key in data.errors) {
$('#id_'+key).html("<p>" + data.errors[key][0] + "</p>");
};
}
else{
window.location = data.success_url;
}
}
});
</script>
{% endblock js_block %}

View file

@ -0,0 +1,57 @@
{% extends 'base.html' %}
{% load staticfiles %}
{% load thumbnail %}
{% block content%}
<!-- OPPORTUNITY VIEW STARTS -->
<div class="main_container" id="maincontainer">
<div class="overview_form_block row marl justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-body" id="datashow">
<div class="panel-heading card-title view-pad text-center">
<h5>
Document
</h5>
<a href="{% url 'common:edit_doc' doc_obj.id %}" class="btn btn-success edit">Edit</a>
</div>
<div class="row marl no-gutters">
<div class="col-md-8">
{% if doc_obj.title %}
<div class="filter_col col-md-12">
<div class="form-group">
<label class="contact_field_label" for="id_title" data-name="title">Title</label>
<div class="contact_field" id="contact_name" data-name="name">{{ doc_obj.title }} </div>
</div>
</div>
{% endif %}
<div class="filter_col col-md-12">
<div class="form-group">
<label class="contact_field_label" for="id_created_by" data-name="created_by">Created By</label>
<div class="contact_field" id="created_by" data-name="created_by">{{ doc_obj.created_by }}</div>
</div>
</div>
<div class="filter_col col-md-12">
<div class="form-group">
<label class="contact_field_label" for="id_created_on" data-name="created_on">Created On</label>
<div class="contact_field" id="created_on" data-name="created_on">{{ doc_obj.created_on }}</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="profile_pic">
{% if doc_obj.document_file %}
<i class="{{file_type_code}}" aria-hidden="true" style="font-size:3rem;"></i>
<!-- <a href="{{doc_obj.document_file.url}}" target="_blank">Download</a> -->
<a style="display:block;" href="{% url 'common:download_document' doc_obj.id %}">Download</a></td>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- OPPORTUNITY VIEW ENDS -->
{% endblock %}

View file

@ -0,0 +1,114 @@
{% extends 'base.html' %}
{% load staticfiles %}
{% load paginate %}
{% block content %}
<!-- main_container starts here -->
<div class="main_container">
<!-- heading_create starts here -->
<div class="row marl heading_create">
<div class="heading col-md-12">
<div class="no-gutters row">
<div class="col-md-6">
</div>
<div class="col-md-6">
<div class="create_new text-right">
<a href="{% url 'common:create_doc'%}"><i class="fa fa-plus"></i> Add New Document</a>
</div>
</div>
</div>
</div>
</div>
<!-- heading_create ends here -->
<!-- filter_row starts here -->
<div class="filter_row row marl">
<div class="col-md-12">
<div class="card">
<div class="card-body">
<div class="card-title">Filters</div>
<form id="docs_filter" action="" method="POST">
<div class="row marl">
<div class="filter_col col-4">
<div class="form-group">
<label for="exampleInputEmail1">Document Name</label>
<input type="text" class="form-control" placeholder="Document name" name="doc_name" value="{{request.POST.doc_name}}">
</div>
</div>
<br clear="all">
</div>
</form>
</div>
</div>
</div>
</div>
<!-- filter_row ends here -->
<div class="table_container_row row marl">
<div class="col-md-12">
<div class="card">
<div class="card-body">
<div class="card-title list">
List of Documents
<span class="float-right total_count">{% if show_pageitems %} {% show_pageitems %}{% else %}Total Documents {{ documents|length }}{% endif %}</span>
</div>
<div class="table-responsive ">
<table class="table table-bordered table-striped table-condensed">
<thead>
{% if documents|length > 0 %}
<tr>
<th width="5%" style="text-align:center;">S.No</th>
<th width="20%" style="text-align:center;">Document Name</th>
<th width="20%" style="text-align:center;">Created_by</th>
<th width="20%" style="text-align:center;">Actions</th>
</tr>
{% endif %}
</thead>
<tbody>
{% if per_page %}
{% paginate per_page documents %}
{% else %}
{% paginate 10 documents %}
{% endif %}
{% for document in documents %}
<tr class="text-center">
<td scope="row">{{ forloop.counter }}</td>
<td><a href="{% url 'common:view_doc' document.id %}">{{ document.title }}</a></td>
<td>{{ document.created_by }}</td>
<td class="actions">
<a href="{% url 'common:edit_doc' document.id %}" class="btn btn-success edit"><i class="fas fa-pencil-alt"></i>Edit</a>
<a href="{% url 'common:remove_doc' document.id %}" class="btn btn-danger trash"><i class="fas fa-trash-alt"></i>Delete</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% ifequal documents|length 0 %}
<h6 class="text-center">No Documents Found</h6>
{% endifequal %}
<div class="marl row text-center">
{% show_pages %}
</div>
</div>
</div>
</div>
</div>
</div>
<!-- main_container ends here -->
{% endblock %}
{% block js_block %}
<script type="text/javascript">
$('.trash').click(function(e){
e.preventDefault()
url = $(this).attr('href')
if (!confirm('Are you sure you want to delete?'))
return;
window.location = $(this).attr('href')
});
$("a[rel='page']").click(function(e){
e.preventDefault();
$('#docs_filter').attr("action", $(this).attr("href"));
$('#docs_filter').submit();
});
</script>
{% endblock js_block %}

View file

@ -3,7 +3,9 @@ from django.contrib.auth import views as auth_views
from django.urls import path, re_path
from common.views import (
HomeView, LoginView, ForgotPasswordView, LogoutView, ChangePasswordView, ProfileView,
UsersListView, CreateUserView, UpdateUserView, UserDetailView, UserDeleteView, PasswordResetView)
UsersListView, CreateUserView, UpdateUserView, UserDetailView, UserDeleteView, PasswordResetView,
DocumentListView, DocumentCreateView, UpdateDocumentView, DocumentDetailView, DocumentDeleteView,
download_document)
from django.conf.urls.static import static
from django.conf import settings
@ -33,5 +35,14 @@ urlpatterns = [
'reset/<uidb64>/<token>/', auth_views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'),
path('reset/done/', auth_views.PasswordResetCompleteView.as_view(), name='password_reset_complete'),
# Document
path('documents/list/', DocumentListView.as_view(), name='doc_list'),
path('documents/create/', DocumentCreateView.as_view(), name='create_doc'),
path('documents/<int:pk>/edit/', UpdateDocumentView.as_view(), name="edit_doc"),
path('documents/<int:pk>/view/', DocumentDetailView.as_view(), name='view_doc'),
path('documents/<int:pk>/delete/', DocumentDeleteView.as_view(), name='remove_doc'),
# download
path('documents/<int:pk>/download/', download_document, name='download_document'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View file

@ -1,12 +1,13 @@
import os
from django.contrib.auth import logout, authenticate, login
from django.contrib.auth.hashers import check_password
from django.contrib.auth.mixins import LoginRequiredMixin, AccessMixin
from django.http import HttpResponseRedirect, JsonResponse
from django.http import HttpResponseRedirect, JsonResponse, HttpResponse, Http404
from django.shortcuts import render, redirect, get_object_or_404
from django.views.generic import (
CreateView, UpdateView, DetailView, TemplateView, View, DeleteView)
from common.models import User
from common.forms import UserForm, LoginForm, ChangePasswordForm, PasswordResetEmailForm
from common.models import User, Document
from common.forms import UserForm, LoginForm, ChangePasswordForm, PasswordResetEmailForm,DocumentForm
from django.contrib.auth.views import PasswordResetView
from django.urls import reverse_lazy
from django.conf import settings
@ -237,3 +238,125 @@ class PasswordResetView(PasswordResetView):
template_name = 'registration/password_reset_form.html'
form_class = PasswordResetEmailForm
email_template_name = 'registration/password_reset_email.html'
class DocumentCreateView(AdminRequiredMixin, CreateView):
model = Document
form_class = DocumentForm
template_name = "doc_create.html"
def form_valid(self, form):
print('heeee')
doc = form.save(commit=False)
doc.created_by = self.request.user
doc.save()
print('doc saved')
if self.request.is_ajax():
data = {'success_url': reverse_lazy('common:doc_list'), 'error': False}
return JsonResponse(data)
return super(DocumentCreateView, self).form_valid(form)
def form_invalid(self, form):
response = super(DocumentCreateView, self).form_invalid(form)
if self.request.is_ajax():
print(form.errors, 'qeeee')
return JsonResponse({'error': True, 'errors': form.errors})
return response
def get_context_data(self, **kwargs):
context = super(DocumentCreateView, self).get_context_data(**kwargs)
context["doc_form"] = context["form"]
if "errors" in kwargs:
context["errors"] = kwargs["errors"]
return context
class DocumentListView(AdminRequiredMixin, TemplateView):
model = Document
context_object_name = "documents"
template_name = "doc_list.html"
def get_queryset(self):
queryset = self.model.objects.all()
request_post = self.request.POST
if request_post:
if request_post.get('doc_name'):
queryset = queryset.filter(title__icontains=request_post.get('doc_name'))
return queryset
def get_context_data(self, **kwargs):
context = super(DocumentListView, self).get_context_data(**kwargs)
context["documents"] = self.get_queryset()
context["per_page"] = self.request.POST.get('per_page')
return context
def post(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
return self.render_to_response(context)
class DocumentDeleteView(AdminRequiredMixin, DeleteView):
model = Document
def get(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.delete()
return redirect("common:doc_list")
class UpdateDocumentView(LoginRequiredMixin, UpdateView):
model = Document
form_class = DocumentForm
template_name = "doc_create.html"
def form_valid(self, form):
doc = form.save(commit=False)
doc.save()
if self.request.is_ajax():
data = {'success_url': reverse_lazy('common:doc_list'), 'error': False}
return JsonResponse(data)
return super(UpdateDocumentView, self).form_valid(form)
def form_invalid(self, form):
response = super(UpdateDocumentView, self).form_invalid(form)
if self.request.is_ajax():
return JsonResponse({'error': True, 'errors': form.errors})
return response
def get_context_data(self, **kwargs):
context = super(UpdateDocumentView, self).get_context_data(**kwargs)
context["doc_obj"] = self.object
context["doc_form"] = context["form"]
if "errors" in kwargs:
context["errors"] = kwargs["errors"]
return context
class DocumentDetailView(AdminRequiredMixin, DetailView):
model = Document
template_name = "doc_detail.html"
def get_context_data(self, **kwargs):
context = super(DocumentDetailView, self).get_context_data(**kwargs)
documents = Document.objects.all()
context.update({
"file_type_code": self.object.file_type()[1],
"doc_obj": self.object,
})
return context
def download_document(request, pk):
doc_obj = Document.objects.filter(id=pk).last()
path = doc_obj.document_file.path
file_path = os.path.join(settings.MEDIA_ROOT, path)
if os.path.exists(file_path):
with open(file_path, 'rb') as fh:
response = HttpResponse(fh.read(), content_type="application/vnd.ms-excel")
response['Content-Disposition'] = 'inline; filename=' + os.path.basename(file_path)
return response
raise Http404

View file

@ -37,6 +37,7 @@
<li class="nav-item"><a class="nav-link" id="leads" href="{% url 'leads:list' %}">Leads</a></li>
<li class="nav-item"><a class="nav-link" id="opportunities"href="{% url 'opportunities:list' %}">Opportunities</a></li>
<li class="nav-item"><a class="nav-link" id="cases" href="{% url 'cases:list' %}">Cases</a></li>
<li class="nav-item"><a class="nav-link" id="documents" href="{% url 'common:doc_list' %}">Documents</a></li>
</ul>
<ul class="navbar-nav navbar-right my-2 my-lg-0">