Skip to content

Commit

Permalink
Merge pull request #524 from CityOfNewYork/develop
Browse files Browse the repository at this point in the history
OpenRecords v3.4.3
  • Loading branch information
johnyu95 authored May 28, 2020
2 parents 949c846 + d116134 commit 7d0c499
Show file tree
Hide file tree
Showing 74 changed files with 738 additions and 356 deletions.
26 changes: 25 additions & 1 deletion app/agency/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
get_letter_templates,
get_reasons
)
from app.models import CustomRequestForms
from app.models import Agencies, CustomRequestForms
import json
from sqlalchemy import asc

Expand Down Expand Up @@ -161,3 +161,27 @@ def get_custom_request_form_fields():
data['tooltips'] = tooltips
data['error_messages'] = error_messages
return jsonify(data), 200


@agency_api_blueprint.route('/request_types/<string:agency_ein>', methods=['GET'])
def get_request_types(agency_ein):
"""
Retrieve the request types (custom request form names) for the specified agency.
:param agency_ein: Agency EIN (String)
:return: JSON Object({"request_type": [('', 'All'), ('Form Name', 'Form Name')]}), 200
"""
if current_user.is_agency_active(agency_ein):
agency = Agencies.query.filter_by(ein=agency_ein).one_or_none()
if agency is not None and agency.agency_features['custom_request_forms']['enabled']:
request_types = [
(custom_request_form.form_name, custom_request_form.form_name)
for custom_request_form in CustomRequestForms.query.filter_by(
agency_ein=agency_ein
).order_by(asc(CustomRequestForms.category), asc(CustomRequestForms.id)).all()
]
request_types.insert(0, ("", "All"))
return jsonify({"request_types": request_types}), 200

return jsonify({}), 404
3 changes: 2 additions & 1 deletion app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,7 @@ def es_create(self):
),
"requester_name": self.requester.name,
"public_title": "Private" if self.privacy["title"] else self.title,
"request_type": [metadata["form_name"] for metadata in self.custom_metadata.values()],
},
)

Expand Down Expand Up @@ -1538,7 +1539,7 @@ class Notes(Responses):
__tablename__ = response_type.NOTE
__mapper_args__ = {"polymorphic_identity": response_type.NOTE}
id = db.Column(db.Integer, db.ForeignKey(Responses.id), primary_key=True)
content = db.Column(db.String(5000))
content = db.Column(db.String)

def __init__(
self, request_id, privacy, content, date_modified=None, is_editable=False
Expand Down
12 changes: 11 additions & 1 deletion app/request/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
response_type,
)
from app.lib.db_utils import get_agency_choices
from app.models import Reasons, LetterTemplates, EnvelopeTemplates
from app.models import Reasons, LetterTemplates, EnvelopeTemplates, CustomRequestForms


class PublicUserRequestForm(Form):
Expand Down Expand Up @@ -437,6 +437,7 @@ class SearchRequestsForm(Form):
# TODO: Add class docstring
agency_ein = SelectField("Agency")
agency_user = SelectField("User")
request_type = SelectField("Request Type", choices=[])

# category = SelectField('Category', get_categories())

Expand Down Expand Up @@ -487,6 +488,15 @@ def __init__(self):
]
self.agency_user.default = current_user.get_id()

if default_agency.agency_features["custom_request_forms"]["enabled"]:
self.request_type.choices = [
(custom_request_form.form_name, custom_request_form.form_name)
for custom_request_form in CustomRequestForms.query.filter_by(
agency_ein=default_agency.ein
).order_by(asc(CustomRequestForms.category), asc(CustomRequestForms.id)).all()
]
self.request_type.choices.insert(0, ("", "All"))

# process form for default values
self.process()

Expand Down
17 changes: 16 additions & 1 deletion app/search/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ def create_index():
"format": "strict_date_hour_minute_second",
},
"assigned_users": {"type": "keyword"},
"request_type": {"type": "keyword"},
}
}
}
Expand Down Expand Up @@ -135,6 +136,9 @@ def create_docs():
if r.date_created < r.date_submitted
else r.date_submitted.strftime(ES_DATETIME_FORMAT)
)
request_type = []
if r.custom_metadata is not None:
request_type = [metadata["form_name"] for metadata in r.custom_metadata.values()]
operation = {
"_op_type": "create",
"_id": r.id,
Expand All @@ -157,7 +161,8 @@ def create_docs():
"public_title": "Private" if not r.privacy["title"] else r.title,
"assigned_users": [
"{guid}".format(guid=user.guid) for user in r.agency_users
]
],
"request_type": request_type
# public_agency_request_summary
}

Expand Down Expand Up @@ -195,6 +200,7 @@ def search_requests(
date_closed_to,
agency_ein,
agency_user_guid,
request_type,
open_,
closed,
in_progress,
Expand Down Expand Up @@ -231,6 +237,7 @@ def search_requests(
:param date_closed_to: date closed to
:param agency_ein: agency ein to filter by
:param agency_user_guid: user (agency) guid to filter by
:param request_type: request type to filter by
:param open_: filter by opened requests?
:param closed: filter by closed requests?
:param in_progress: filter by in-progress requests?
Expand Down Expand Up @@ -368,6 +375,7 @@ def datestr_local_to_utc(datestr):
date_ranges,
agency_ein,
agency_user_guid,
request_type,
match_type,
)
if foil_id:
Expand Down Expand Up @@ -429,6 +437,7 @@ def datestr_local_to_utc(datestr):
"agency_request_summary",
"description",
"assigned_users",
"request_type",
],
size=result_set_size,
from_=start,
Expand Down Expand Up @@ -460,6 +469,7 @@ def datestr_local_to_utc(datestr):
"agency_request_summary",
"description",
"assigned_users",
"request_type"
],
size=result_set_size,
from_=start,
Expand Down Expand Up @@ -496,6 +506,7 @@ def __init__(
date_ranges,
agency_ein,
agency_user_guid,
request_type,
match_type,
):
self.__query = query
Expand All @@ -513,6 +524,10 @@ def __init__(
self.__default_filters.append(
{"term": {"assigned_users": agency_user_guid}}
)
if request_type:
self.__default_filters.append(
{"term": {"request_type": request_type}}
)

self.__filters = []
self.__conditions = []
Expand Down
2 changes: 2 additions & 0 deletions app/search/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ def requests():
date_closed_to=request.args.get("date_closed_to"),
agency_ein=agency_ein,
agency_user_guid=request.args.get("agency_user"),
request_type=request.args.get("request_type"),
open_=eval_request_bool(request.args.get("open")),
closed=eval_request_bool(request.args.get("closed")),
in_progress=eval_request_bool(request.args.get("in_progress"))
Expand Down Expand Up @@ -215,6 +216,7 @@ def requests_doc(doc_type):
date_closed_to=request.args.get("date_closed_to"),
agency_ein=agency_ein,
agency_user_guid=request.args.get("agency_user"),
request_type=request.args.get("request_type"),
open_=eval_request_bool(request.args.get("open")),
closed=eval_request_bool(request.args.get("closed")),
in_progress=eval_request_bool(request.args.get("in_progress"))
Expand Down
92 changes: 91 additions & 1 deletion app/static/js/plugins/tinymce/jquery.tinymce.min.js
Original file line number Diff line number Diff line change
@@ -1 +1,91 @@
!function(a){function b(){function b(a){"remove"===a&&this.each(function(a,b){var c=e(b);c&&c.remove()}),this.find("span.mceEditor,div.mceEditor").each(function(a,b){var c=tinymce.get(b.id.replace(/_parent$/,""));c&&c.remove()})}function d(a){var c,d=this;if(null!=a)b.call(d),d.each(function(b,c){var d;(d=tinymce.get(c.id))&&d.setContent(a)});else if(d.length>0&&(c=tinymce.get(d[0].id)))return c.getContent()}function e(a){var b=null;return a&&a.id&&g.tinymce&&(b=tinymce.get(a.id)),b}function f(a){return!!(a&&a.length&&g.tinymce&&a.is(":tinymce"))}var h={};a.each(["text","html","val"],function(b,g){var i=h[g]=a.fn[g],j="text"===g;a.fn[g]=function(b){var g=this;if(!f(g))return i.apply(g,arguments);if(b!==c)return d.call(g.filter(":tinymce"),b),i.apply(g.not(":tinymce"),arguments),g;var h="",k=arguments;return(j?g:g.eq(0)).each(function(b,c){var d=e(c);h+=d?j?d.getContent().replace(/<(?:"[^"]*"|'[^']*'|[^'">])*>/g,""):d.getContent({save:!0}):i.apply(a(c),k)}),h}}),a.each(["append","prepend"],function(b,d){var g=h[d]=a.fn[d],i="prepend"===d;a.fn[d]=function(a){var b=this;return f(b)?a!==c?("string"==typeof a&&b.filter(":tinymce").each(function(b,c){var d=e(c);d&&d.setContent(i?a+d.getContent():d.getContent()+a)}),g.apply(b.not(":tinymce"),arguments),b):void 0:g.apply(b,arguments)}}),a.each(["remove","replaceWith","replaceAll","empty"],function(c,d){var e=h[d]=a.fn[d];a.fn[d]=function(){return b.call(this,d),e.apply(this,arguments)}}),h.attr=a.fn.attr,a.fn.attr=function(b,g){var i=this,j=arguments;if(!b||"value"!==b||!f(i))return g!==c?h.attr.apply(i,j):h.attr.apply(i,j);if(g!==c)return d.call(i.filter(":tinymce"),g),h.attr.apply(i.not(":tinymce"),j),i;var k=i[0],l=e(k);return l?l.getContent({save:!0}):h.attr.apply(a(k),j)}}var c,d,e,f=[],g=window;a.fn.tinymce=function(c){function h(){var d=[],f=0;e||(b(),e=!0),l.each(function(a,b){var e,g=b.id,h=c.oninit;g||(b.id=g=tinymce.DOM.uniqueId()),tinymce.get(g)||(e=new tinymce.Editor(g,c,tinymce.EditorManager),d.push(e),e.on("init",function(){var a,b=h;l.css("visibility",""),h&&++f==d.length&&("string"==typeof b&&(a=-1===b.indexOf(".")?null:tinymce.resolve(b.replace(/\.\w+$/,"")),b=tinymce.resolve(b)),b.apply(a||tinymce,d))}))}),a.each(d,function(a,b){b.render()})}var i,j,k,l=this,m="";if(!l.length)return l;if(!c)return window.tinymce?tinymce.get(l[0].id):null;if(l.css("visibility","hidden"),g.tinymce||d||!(i=c.script_url))1===d?f.push(h):h();else{d=1,j=i.substring(0,i.lastIndexOf("/")),-1!=i.indexOf(".min")&&(m=".min"),g.tinymce=g.tinyMCEPreInit||{base:j,suffix:m},-1!=i.indexOf("gzip")&&(k=c.language||"en",i=i+(/\?/.test(i)?"&":"?")+"js=true&core=true&suffix="+escape(m)+"&themes="+escape(c.theme||"modern")+"&plugins="+escape(c.plugins||"")+"&languages="+(k||""),g.tinyMCE_GZ||(g.tinyMCE_GZ={start:function(){function b(a){tinymce.ScriptLoader.markDone(tinymce.baseURI.toAbsolute(a))}b("langs/"+k+".js"),b("themes/"+c.theme+"/theme"+m+".js"),b("themes/"+c.theme+"/langs/"+k+".js"),a.each(c.plugins.split(","),function(a,c){c&&(b("plugins/"+c+"/plugin"+m+".js"),b("plugins/"+c+"/langs/"+k+".js"))})},end:function(){}}));var n=document.createElement("script");n.type="text/javascript",n.onload=n.onreadystatechange=function(b){b=b||window.event,2===d||"load"!=b.type&&!/complete|loaded/.test(n.readyState)||(tinymce.dom.Event.domLoaded=1,d=2,c.script_loaded&&c.script_loaded(),h(),a.each(f,function(a,b){b()}))},n.src=i,document.body.appendChild(n)}return l},a.extend(a.expr[":"],{tinymce:function(a){var b;return!!(a.id&&"tinymce"in window&&(b=tinymce.get(a.id),b&&b.editorManager===tinymce))}})}(jQuery);
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
/**
* Jquery integration plugin.
*
* @class tinymce.core.JqueryIntegration
* @private
*/
!function(){var f,c,u,p,d,s=[];d="undefined"!=typeof global?global:window,p=d.jQuery;function v(){
// Reference to tinymce needs to be lazily evaluated since tinymce
// might be loaded through the compressor or other means
return d.tinymce}p.fn.tinymce=function(o){var e,t,i,l=this,r="";
// No match then just ignore the call
if(!l.length)return l;
// Get editor instance
if(!o)return v()?v().get(l[0].id):null;l.css("visibility","hidden");function n(){var a=[],c=0;
// Apply patches to the jQuery object, only once
u||(m(),u=!0),
// Create an editor instance for each matched node
l.each(function(e,t){var n,i=t.id,r=o.oninit;
// Generate unique id for target element if needed
i||(t.id=i=v().DOM.uniqueId()),
// Only init the editor once
v().get(i)||(
// Create editor instance and render it
n=v().createEditor(i,o),a.push(n),n.on("init",function(){var e,t=r;l.css("visibility",""),
// Run this if the oninit setting is defined
// this logic will fire the oninit callback ones each
// matched editor instance is initialized
r&&++c==a.length&&("string"==typeof t&&(e=-1===t.indexOf(".")?null:v().resolve(t.replace(/\.\w+$/,"")),t=v().resolve(t)),
// Call the oninit function with the object
t.apply(e||v(),a))}))}),
// Render the editor instances in a separate loop since we
// need to have the full editors array used in the onInit calls
p.each(a,function(e,t){t.render()})}
// Load TinyMCE on demand, if we need to
if(d.tinymce||c||!(e=o.script_url))
// Delay the init call until tinymce is loaded
1===c?s.push(n):n();else{c=1,t=e.substring(0,e.lastIndexOf("/")),
// Check if it's a dev/src version they want to load then
// make sure that all plugins, themes etc are loaded in source mode as well
-1!=e.indexOf(".min")&&(r=".min"),
// Setup tinyMCEPreInit object this will later be used by the TinyMCE
// core script to locate other resources like CSS files, dialogs etc
// You can also predefined a tinyMCEPreInit object and then it will use that instead
d.tinymce=d.tinyMCEPreInit||{base:t,suffix:r},
// url contains gzip then we assume it's a compressor
-1!=e.indexOf("gzip")&&(i=o.language||"en",e=e+(/\?/.test(e)?"&":"?")+"js=true&core=true&suffix="+escape(r)+"&themes="+escape(o.theme||"modern")+"&plugins="+escape(o.plugins||"")+"&languages="+(i||""),
// Check if compressor script is already loaded otherwise setup a basic one
d.tinyMCE_GZ||(d.tinyMCE_GZ={start:function(){function n(e){v().ScriptLoader.markDone(v().baseURI.toAbsolute(e))}
// Add core languages
n("langs/"+i+".js"),
// Add themes with languages
n("themes/"+o.theme+"/theme"+r+".js"),n("themes/"+o.theme+"/langs/"+i+".js"),
// Add plugins with languages
p.each(o.plugins.split(","),function(e,t){t&&(n("plugins/"+t+"/plugin"+r+".js"),n("plugins/"+t+"/langs/"+i+".js"))})},end:function(){}}));var a=document.createElement("script");a.type="text/javascript",a.onload=a.onreadystatechange=function(e){e=e||window.event,2===c||"load"!=e.type&&!/complete|loaded/.test(a.readyState)||(v().dom.Event.domLoaded=1,c=2,
// Execute callback after mainscript has been loaded and before the initialization occurs
o.script_loaded&&o.script_loaded(),n(),p.each(s,function(e,t){t()}))},a.src=e,document.body.appendChild(a)}return l},
// Add :tinymce pseudo selector this will select elements that has been converted into editor instances
// it's now possible to use things like $('*:tinymce') to get all TinyMCE bound elements.
p.extend(p.expr[":"],{tinymce:function(e){var t;return!!(e.id&&"tinymce"in d&&(t=v().get(e.id))&&t.editorManager===v())}});
// This function patches internal jQuery functions so that if
// you for example remove an div element containing an editor it's
// automatically destroyed by the TinyMCE API
var m=function(){function r(e){
// If the function is remove
"remove"===e&&this.each(function(e,t){var n=u(t);n&&n.remove()}),this.find("span.mceEditor,div.mceEditor").each(function(e,t){var n=v().get(t.id.replace(/_parent$/,""));n&&n.remove()})}function o(i){var e,t=this;
// Handle set value
/*jshint eqnull:true */if(null!=i)r.call(t),
// Saves the contents before get/set value of textarea/div
t.each(function(e,t){var n;(n=v().get(t.id))&&n.setContent(i)});else if(0<t.length&&(e=v().get(t[0].id)))return e.getContent()}function l(e){return!!(e&&e.length&&d.tinymce&&e.is(":tinymce"))}
// Removes any child editor instances by looking for editor wrapper elements
var u=function(e){var t=null;return e&&e.id&&d.tinymce&&(t=v().get(e.id)),t},s={};
// Loads or saves contents from/to textarea if the value
// argument is defined it will set the TinyMCE internal contents
// Patch some setter/getter functions these will
// now be able to set/get the contents of editor instances for
// example $('#editorid').html('Content'); will update the TinyMCE iframe instance
p.each(["text","html","val"],function(e,t){var a=s[t]=p.fn[t],c="text"===t;p.fn[t]=function(e){var t=this;if(!l(t))return a.apply(t,arguments);if(e!==f)return o.call(t.filter(":tinymce"),e),a.apply(t.not(":tinymce"),arguments),t;// return original set for chaining
var i="",r=arguments;return(c?t:t.eq(0)).each(function(e,t){var n=u(t);i+=n?c?n.getContent().replace(/<(?:"[^"]*"|'[^']*'|[^'">])*>/g,""):n.getContent({save:!0}):a.apply(p(t),r)}),i}}),
// Makes it possible to use $('#id').append("content"); to append contents to the TinyMCE editor iframe
p.each(["append","prepend"],function(e,t){var n=s[t]=p.fn[t],r="prepend"===t;p.fn[t]=function(i){var e=this;return l(e)?i!==f?("string"==typeof i&&e.filter(":tinymce").each(function(e,t){var n=u(t);n&&n.setContent(r?i+n.getContent():n.getContent()+i)}),n.apply(e.not(":tinymce"),arguments),e):void 0:n.apply(e,arguments)}}),
// Makes sure that the editor instance gets properly destroyed when the parent element is removed
p.each(["remove","replaceWith","replaceAll","empty"],function(e,t){var n=s[t]=p.fn[t];p.fn[t]=function(){return r.call(this,t),n.apply(this,arguments)}}),s.attr=p.fn.attr,
// Makes sure that $('#tinymce_id').attr('value') gets the editors current HTML contents
p.fn.attr=function(e,t){var n=this,i=arguments;if(!e||"value"!==e||!l(n))return s.attr.apply(n,i);if(t!==f)return o.call(n.filter(":tinymce"),t),s.attr.apply(n.not(":tinymce"),i),n;// return original set for chaining
var r=n[0],a=u(r);return a?a.getContent({save:!0}):s.attr.apply(p(r),i)}}}();
3 changes: 3 additions & 0 deletions app/static/js/plugins/tinymce/langs/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This is where language files should be placed.

Please DO NOT translate these directly use this service: https://www.transifex.com/projects/p/tinymce/
12 changes: 6 additions & 6 deletions app/static/js/plugins/tinymce/license.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999

Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Expand All @@ -10,7 +10,7 @@
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]

Preamble
Preamble

The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Expand Down Expand Up @@ -112,7 +112,7 @@ modification follow. Pay close attention to the difference between a
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

GNU LESSER GENERAL PUBLIC LICENSE
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

0. This License Agreement applies to any software library or other
Expand Down Expand Up @@ -432,7 +432,7 @@ decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.

NO WARRANTY
NO WARRANTY

15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
Expand All @@ -455,7 +455,7 @@ FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.

END OF TERMS AND CONDITIONS
END OF TERMS AND CONDITIONS

How to Apply These Terms to Your New Libraries

Expand Down
9 changes: 9 additions & 0 deletions app/static/js/plugins/tinymce/plugins/lists/plugin.min.js

Large diffs are not rendered by default.

Loading

0 comments on commit 7d0c499

Please sign in to comment.