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:
parent
0cc376e240
commit
847272ce32
10 changed files with 564 additions and 22 deletions
17
README.rst
17
README.rst
|
|
@ -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`_.
|
||||
|
|
|
|||
|
|
@ -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']
|
||||
26
common/migrations/0003_document.py
Normal file
26
common/migrations/0003_document.py
Normal 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)),
|
||||
],
|
||||
),
|
||||
]
|
||||
148
common/models.py
148
common/models.py
|
|
@ -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
|
||||
72
common/templates/doc_create.html
Normal file
72
common/templates/doc_create.html
Normal 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 %}
|
||||
57
common/templates/doc_detail.html
Normal file
57
common/templates/doc_detail.html
Normal 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 %}
|
||||
114
common/templates/doc_list.html
Normal file
114
common/templates/doc_list.html
Normal 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 %}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
129
common/views.py
129
common/views.py
|
|
@ -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
|
||||
|
|
@ -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">
|
||||
|
|
|
|||
Loading…
Reference in a new issue