diff --git a/.env.example b/.env.example index 51a2b463..8b768a42 100644 --- a/.env.example +++ b/.env.example @@ -28,7 +28,7 @@ MAIL_PORT=25 # IT Intake Form APP_DEV_INTAKE_EMAIL_RECIPIENTS=admin@records.nyc.gov,pm@records.nyc.gov - +IT_INTAKE_EMAIL_RECIPIENTS=admin@records.nyc.gov,it@records.nyc.gov # File Uplaod FILE_UPLOAD_PATH= diff --git a/.gitignore b/.gitignore index 6b2a6690..20ef82ce 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ app/static/documents/ !app/static/documents/.gitkeep saml/*.json app/static/img/carousel/ -app/static/files/orientation.mp4 +app/static/files/*.mp4 # Byte-compiled / optimized / DLL files __pycache__/ @@ -113,4 +113,72 @@ ENV/ .mypy_cache/ # vagrant -Vagrantfile \ No newline at end of file +Vagrantfile +.vagrant/ + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 00000000..a55e7a17 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 00000000..7aea66b8 --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,11 @@ + + + + + postgresql + true + org.postgresql.Driver + jdbc:postgresql://localhost:9432/intranet + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 00000000..15a15b21 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/intranet.iml b/.idea/intranet.iml new file mode 100644 index 00000000..2ab33731 --- /dev/null +++ b/.idea/intranet.iml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..615b3916 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..b10b5044 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..94a25f7f --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/main/forms.py b/app/main/forms.py index 662e8d07..9b666b55 100644 --- a/app/main/forms.py +++ b/app/main/forms.py @@ -161,6 +161,84 @@ class EnfgForm(FlaskForm): submit = SubmitField('Print') +class ITIntakeForm(FlaskForm): + # Submission Information + submitter_name = StringField("Submitter Name:", validators=[DataRequired()]) + submitter_email = EmailField("Submitter Email:", validators=[DataRequired()]) + submitter_phone = TelField("Submitter Phone:", validators=[DataRequired()]) + submitter_title = StringField("Submitter Title", validators=[DataRequired()]) + submitter_division = StringField( + "Submitter Division:", validators=[DataRequired()] + ) + + # Project Information + project_name = StringField("Name:", validators=[DataRequired()]) + enhancement_or_new_project = SelectField( + "Is this a new project or an enhancement to an existing project?", + choices=PROJECT_TYPE, + validators=[DataRequired()], + ) + current_project_name = StringField( + "If this is an enhancement, please provide the name of the current project:", + validators=[ + RequiredIf( + enhancement_or_new_project=ENHANCEMENT, + message="You must provide the current project name if this is an enhancement", + ) + ], + ) + project_background = TextAreaField("Background:", validators=[DataRequired()]) + rationale = TextAreaField("Rationale:", validators=[DataRequired()]) + project_goals = TextAreaField("Goals:", validators=[DataRequired()]) + priority = SelectField("Priority:", choices=PRIORITY, validators=[DataRequired()]) + completion_date = DateField( + "When do you want this project to be delivered?", validators=[DataRequired()] + ) + supplemental_materials_one = FileField("Supplemental Materials:") + supplemental_materials_one_desc = StringField( + "Supplemental Materials Description: ", + validators=[RequiredIf(supplemental_materials_one != "None")], + ) + supplemental_materials_two = FileField("Supplemental Materials") + supplemental_materials_two_desc = StringField( + "Supplemental Materials Description: ", + validators=[RequiredIf(supplemental_materials_two != "None")], + ) + supplemental_materials_three = FileField("Supplemental Materials:") + supplemental_materials_three_desc = StringField( + "Supplemental Materials Description: ", + validators=[RequiredIf(supplemental_materials_three != "None")], + ) + designated_business_owner_name = StringField( + "Designated Business Owner Name:", validators=[DataRequired()] + ) + designated_business_owner_email = EmailField( + "Designated Business Owner Email:", validators=[DataRequired()] + ) + designated_business_owner_phone = TelField( + "Designated Business Owner Phone:", validators=[DataRequired()] + ) + designated_business_owner_title = StringField( + "Designated Business Owner Title", validators=[DataRequired()] + ) + designated_business_owner_division = SelectField( + "Designated Business Owner Division:", + choices=choices.DIVISIONS, + validators=[DataRequired()], + ) + + # Technical Information + dependencies = TextAreaField("Does this project depend on other projects or applications? If so, please elaborate") + location_manhattan = BooleanField("31 Chambers") + location_queens = BooleanField("Queens Warehouse") + location_brooklyn = BooleanField("Bush Terminal Warehouse") + + # Submit + submit = SubmitField("Submit Intake Request") + + + + class AppDevIntakeForm(FlaskForm): # Submission Information submitter_name = StringField("Submitter Name:", validators=[DataRequired()]) diff --git a/app/main/views.py b/app/main/views.py index 5c4c7923..931605fa 100644 --- a/app/main/views.py +++ b/app/main/views.py @@ -2,19 +2,22 @@ from flask_login import login_required, current_user from app.models import Users, Posts, EventPosts, Documents from . import main -from app.main.forms import MeetingNotesForm, NewsForm, EventForm, StaffDirectorySearchForm, EnfgForm, UploadForm, AppDevIntakeForm -from app.main.utils import (create_meeting_notes, - create_news, - create_event_post, - get_users_by_division, - get_rooms_by_division, - create_document, - allowed_file, - VirusDetectedException, - scan_file, - process_documents_search, - process_posts_search, - render_email) +from app.main.forms import MeetingNotesForm, NewsForm, EventForm, StaffDirectorySearchForm, EnfgForm, UploadForm, \ + AppDevIntakeForm, ITIntakeForm +from app.main.utils import ( + create_meeting_notes, + create_news, + create_event_post, + get_users_by_division, + get_rooms_by_division, + create_document, + allowed_file, + VirusDetectedException, + scan_file, + process_documents_search, + process_posts_search, + render_email +) from datetime import datetime from io import BytesIO import pytz @@ -74,7 +77,8 @@ def news_and_updates(): sorted_tags.append((key, tags_counter[key])) search_term = flask_request.args.get('search_term', None) - return render_template('news_and_updates.html', tags=sorted_tags, search_term=search_term, posts_counter=posts_counter) + return render_template('news_and_updates.html', tags=sorted_tags, search_term=search_term, + posts_counter=posts_counter) @main.route('/posts/search/', methods=['GET']) @@ -147,7 +151,8 @@ def meeting_notes(): # Get meeting type choices meeting_types = choices.MEETING_TYPES[1::] - return render_template('meeting_notes.html', posts=posts, meeting_types=meeting_types, tags=sorted_tags, meeting_type_counter=meeting_type_counter) + return render_template('meeting_notes.html', posts=posts, meeting_types=meeting_types, tags=sorted_tags, + meeting_type_counter=meeting_type_counter) @main.route('/news-and-updates/news', methods=['GET']) @@ -637,6 +642,75 @@ def app_dev_intake_form(): return render_template("app_dev_intake.html", form=form, current_user=current_user) +@main.route('/it-support/it-intake-form', methods=['GET', 'POST']) +@login_required +def it_intake_form(): + """ + View function to handle the IT Intake Form. + + GET Request: + Returns the html template for the Intake Form + + POST Request: + Handles submission of Intake form. If it is validated, redirect to the IT Support page + """ + form = ITIntakeForm() + + # Pre-Fill the Submitter Information - Cannot be edited + form.submitter_name.data = current_user.name + form.submitter_email.data = current_user.email + form.submitter_phone.data = current_user.phone_number + form.submitter_title.data = current_user.title + form.submitter_division.data = current_user.division + + if flask_request.method == 'GET': + # Pre-Fill the Designated Business Owner Information with the Submitters Information - Can be Edited + form.designated_business_owner_name.data = current_user.name + form.designated_business_owner_email.data = current_user.email + form.designated_business_owner_phone.data = current_user.phone_number + form.designated_business_owner_title.data = current_user.title + form.designated_business_owner_division.data = current_user.division + + if form.validate_on_submit(): + email = render_email(form.data, 'email/email_it_intake.html') + sender = form.submitter_email.data + recipients = current_app.config['IT_INTAKE_EMAIL_RECIPIENTS'] + [form.submitter_email.data, + form.designated_business_owner_email.data] + msg = Message( + "IT Intake Form - {project_name}".format( + project_name=form.project_name.data + ), + sender=sender, + recipients=recipients, + ) + msg.html = email + if form.supplemental_materials_one.data is not None: + tmp_file = BytesIO() + flask_request.files['supplemental_materials_one'].save(tmp_file) + tmp_file.seek(0) + msg.attach(filename=form.supplemental_materials_one.data.filename, + content_type=form.supplemental_materials_one.data.content_type, data=tmp_file.read()) + if form.supplemental_materials_two.data is not None: + tmp_file = BytesIO() + flask_request.files['supplemental_materials_two'].save(tmp_file) + tmp_file.seek(0) + msg.attach(filename=form.supplemental_materials_two.data.filename, + content_type=form.supplemental_materials_two.data.content_type, data=tmp_file.read()) + if form.supplemental_materials_three.data is not None: + tmp_file = BytesIO() + flask_request.files['supplemental_materials_three'].save(tmp_file) + tmp_file.seek(0) + msg.attach(filename=form.supplemental_materials_three.data.filename, + content_type=form.supplemental_materials_three.data.content_type, data=tmp_file.read()) + mail.send(msg) + flash("Successfully submitted intake form. Please allow 5 business days for a response.") + return redirect(url_for('main.it_support')) + else: + for error in form.errors.items(): + flash(error[1][0], category="danger") + return render_template("it_intake.html", form=form, current_user=current_user) + + @main.route('/documents', methods=['GET']) def documents(): """ @@ -682,8 +756,10 @@ def search_documents(): document_type='policies-and-procedures', sort_by=sort_by, search_term=search_term, - documents_start=page_counters['policies_and_procedures']['start'], - documents_end=page_counters['policies_and_procedures']['end']) + documents_start=page_counters['policies_and_procedures'][ + 'start'], + documents_end=page_counters['policies_and_procedures'][ + 'end']) templates_data = process_documents_search(document_type_plain_text='Templates', document_type='templates', @@ -759,15 +835,16 @@ def upload_document(): form = UploadForm() if flask_request.method == 'POST' and form.validate_on_submit(): file = flask_request.files['file_object'] - filename = secure_filename(file.filename) # use the secure version of the file name + filename = secure_filename(file.filename) # use the secure version of the file name # Files are unique in both file title and file name, this will be used to check if a file already exists in the database # TODO: check file uniqueness using a checksum instead of just filename - file_exists = Documents.query.filter(or_(Documents.file_name == filename, Documents.file_title == form.file_title.data)).first() or None + file_exists = Documents.query.filter( + or_(Documents.file_name == filename, Documents.file_title == form.file_title.data)).first() or None if file_exists: - if file_exists.file_name == filename: # File with the same name already exists + if file_exists.file_name == filename: # File with the same name already exists flash('This file has already been uploaded. Please select another file.') return render_template('upload_document.html', form=form) - elif file_exists.file_title == form.file_title.data: # file with the same title already exists + elif file_exists.file_title == form.file_title.data: # file with the same title already exists flash('A file with this title has already been uploaded. Please use another title.') return render_template('upload_document.html', form=form) elif not allowed_file(filename): @@ -792,4 +869,4 @@ def upload_document(): division=form.division.data) flash('Document successfully uploaded.') return redirect(url_for('main.documents')) - return render_template('upload_document.html', form=form) \ No newline at end of file + return render_template('upload_document.html', form=form) diff --git a/app/templates/email/email_it_intake.html b/app/templates/email/email_it_intake.html new file mode 100644 index 00000000..85e11bbd --- /dev/null +++ b/app/templates/email/email_it_intake.html @@ -0,0 +1,134 @@ +{% extends "bootstrap/base.html" %} +{% block content %} +
+
+
+

Submission Information

+
+
+
+
Date Submitted:
+
{{ today or "" }}
+
+
+
Submitter Name:
+
{{ form.submitter_name or "" }}
+
+
+
Submitter Email:
+
{{ form.submitter_email or "" }}
+
+
+
Submitter Phone:
+
{{ form.submitter_phone or "" }}
+
+
+
Submitter Title
+
{{ form.submitter_title or "" }}
+
+
+
Submitter Division:
+
{{ form.submitter_division or "" }}
+
+
+
+

Project Information

+
+
+
+
Name:
+
{{ form.project_name or "" }}
+
+
+
Is this a new project or an enhancement to an existing project?
+
{{ form.enhancement_or_new_project or "" }}
+
+
+
If this is an enhancement, please provide the name of the current project:
+
{{ form.current_project_name or "" }}
+
+
+
Background:
+
{{ form.project_background or "" }}
+
+
+
Rationale:
+
{{ form.rationale or "" }}
+
+
+
Goals:
+
{{ form.project_goals or "" }}
+
+
+
Priority:
+
{{ form.priority or "" }}
+
+
+
When do you want this project to be delivered?
+
{{ form.completion_date or "" }}
+
+ {% if form.supplemental_materials_one_desc %} +
+
Supplemental Materials Description:
+
{{ form.supplemental_materials_one_desc }} ({{ form.supplemental_materials_one.filename }})
+
+ {% endif %} + {% if form.supplemental_materials_two_desc %} +
+
Supplemental Materials Description:
+
{{ form.supplemental_materials_two_desc }} ({{ form.supplemental_materials_two.filename }})
+
+ {% if form.supplemental_materials_three_desc %} + {% endif %} +
+
Supplemental Materials Description:
+
{{ form.supplemental_materials_three_desc }} ({{ form.supplemental_materials_three.filename }})
+
+ {% endif %} +
+
+

Business Owner Information

+
+
+
+
Designated Business Owner Name:
+
{{ form.designated_business_owner_name or "" }}
+
+
+
Designated Business Owner Email:
+
{{ form.designated_business_owner_email or "" }}
+
+
+
Designated Business Owner Phone:
+
{{ form.designated_business_owner_phone or "" }}
+
+
+
Designated Business Owner Title
+
{{ form.designated_business_owner_title or "" }}
+
+
+
Designated Business Owner Division:
+
{{ form.designated_business_owner_division or "" }}
+
+ +
+
+

Technical Information

+
+
+
+
Does this project depend on other projects or applications? If so, please elaborate
+
{{ form.dependencies or "" }}
+
+
+
Locations:
+
+
    + {% if form.location_manhattan %}
  • 31 Chambers
  • {% endif %} + {% if form.location_brooklyn %}
  • Bush Terminal Warehouse
  • {% endif %} + {% if form.location_queens %}
  • Queens Warehouse
  • {% endif %} +
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/app/templates/it_intake.html b/app/templates/it_intake.html new file mode 100644 index 00000000..b67de514 --- /dev/null +++ b/app/templates/it_intake.html @@ -0,0 +1,320 @@ +{% extends "base.html" %} +{% import "bootstrap/wtf.html" as wtf %} + +{% block title %}IT - Application Development Intake Form{% endblock %} + +{% block page_content %} +
+ +
+
+ {{ form.csrf_token }} + {% with messages = get_flashed_messages(with_categories=true) %} + {% for category, message in messages %} +
+
+ + {{ message }} +
+
+ {% endfor %} + {% endwith %} +
+
+
+

Submission Information

+
+
+
+
+ +
+ +
+
+
+
 
+
+
+ {{ form.submitter_name.label(class="col-md-4 control-label") }} +
+ {{ form.submitter_name(class="form-control", readonly="") }} +
+
+
+
 
+
+
+ {{ form.submitter_email.label(class="col-md-4 control-label") }} +
+ {{ form.submitter_email(class="form-control", readonly="") }} +
+
+
+
 
+
+
+ {{ form.submitter_phone.label(class="col-md-4 control-label") }} +
+ {{ form.submitter_phone(class="form-control", readonly="") }} +
+
+
+
 
+
+
+ {{ form.submitter_title.label(class="col-md-4 control-label") }} +
+ {{ form.submitter_title(class="form-control", readonly="") }} +
+
+
+
 
+
+
+ {{ form.submitter_division.label(class="col-md-4 control-label") }} +
+ {{ form.submitter_division(class="form-control", readonly="") }} +
+
+ +
+
+
+
+
+
+

Project Information

+
+
+
+
+ {{ form.project_name.label(class="col-md-4 control-label") }} +
+ {{ form.project_name(class="form-control") }} +
+
+
+
 
+
+
+ {{ form.enhancement_or_new_project.label(class="col-md-4 control-label") }} +
+ {{ form.enhancement_or_new_project(class="form-control") }} +
+
+
+
 
+
+
+ {{ form.current_project_name.label(class="col-md-4 control-label") }} +
+ {{ form.current_project_name(class="form-control") }} +
+
+
+
 
+
+
+ {{ form.project_background.label(class="col-md-4 control-label") }} +
+ {{ form.project_background(class="form-control", rows=10) }} +
+
+
+
 
+
+
+ {{ form.rationale.label(class="col-md-4 control-label") }} +
+ {{ form.rationale(class="form-control", rows=10) }} +
+
+
+
 
+
+
+ {{ form.project_goals.label(class="col-md-4 control-label") }} +
+ {{ form.project_goals(class="form-control", rows=10) }} +
+
+
+
 
+
+
+ {{ form.priority.label(class="col-md-4 control-label") }} +
+ {{ form.priority(class="form-control") }} +
+
+
+
 
+
+
+ {{ form.completion_date.label(class="col-md-4 control-label") }} +
+ {{ form.completion_date(class="form-control") }} +
+
+
+
 
+
+
+ {{ form.supplemental_materials_one_desc.label(class="col-md-2 control-label") }} +
+ {{ form.supplemental_materials_one_desc(class="form-control") }} +
+
+ {{ form.supplemental_materials_one(class="form-control") }} +
+
+
+
+
+ {{ form.supplemental_materials_two_desc.label(class="col-md-2 control-label") }} +
+ {{ form.supplemental_materials_two_desc(class="form-control") }} +
+
+ {{ form.supplemental_materials_two(class="form-control") }} +
+
+
+
+
+ {{ form.supplemental_materials_three_desc.label(class="col-md-2 control-label") }} +
+ {{ form.supplemental_materials_three_desc(class="form-control") }} +
+
+ {{ form.supplemental_materials_three(class="form-control") }} +
+
+
+
+
+
+
+
+
+

Business Owner Information

+
+
+
+
+ {{ form.designated_business_owner_name.label(class="col-md-4 control-label") }} +
+ {{ form.designated_business_owner_name(class="form-control") }} +
+
+
+
 
+
+
+ {{ form.designated_business_owner_email.label(class="col-md-4 control-label") }} +
+ {{ form.designated_business_owner_email(class="form-control") }} +
+
+
+
 
+
+
+ {{ form.designated_business_owner_phone.label(class="col-md-4 control-label") }} +
+ {{ form.designated_business_owner_phone(class="form-control") }} +
+
+
+
 
+
+
+ {{ form.designated_business_owner_title.label(class="col-md-4 control-label") }} +
+ {{ form.designated_business_owner_title(class="form-control") }} +
+
+
+
 
+
+
+ {{ form.designated_business_owner_division.label(class="col-md-4 control-label") }} +
+ {{ form.designated_business_owner_division(class="form-control") }} +
+
+
+
+
+
+
+
+
+

Technical Information

+
+
+
+
+ {{ form.dependencies.label(class="col-md-4 control-label") }} +
+ {{ form.dependencies(class="form-control") }} +
+
+
+
 
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+
 
+
 
+
+
+ {{ form.submit(class="btn btn-default") }} +
+
+
+
+
+{% endblock %} + +{% block custom_scripts %} + +{% endblock %} \ No newline at end of file diff --git a/app/templates/it_support.html b/app/templates/it_support.html index d89998e0..5947c696 100644 --- a/app/templates/it_support.html +++ b/app/templates/it_support.html @@ -4,223 +4,249 @@ {% block title %}IT Support{% endblock %} {% block custom_css %} - + {% endblock %} {% block page_content %} -
- - {% for message in get_flashed_messages() %} -
- - {{ message }} -
- {% endfor %} -
-
-
-
-
-

DORIS HELP DESK

-
-
-

Phone: (212) 788-8606

-

Email: hchan@records.nyc.gov

-
+
+ +
+ {% for message in get_flashed_messages() %} +
+ + {{ message }} +
+ {% endfor %} +
+
+
+
+
+

DORIS HELP DESK

+
+
+

Phone: (212) 788-8606

+

Email: hchan@records.nyc.gov

-
-
-
-

CITYWIDE SERVICE DESK

-
-
-

Phone: (718) 403-8888

-

Email: nychelp@doitt.nyc.gov

-
+
+
+
+
+

CITYWIDE SERVICE DESK

+
+
+

Phone: (718) 403-8888

+

Email: nychelp@doitt.nyc.gov

-
-
- + -
-
-
-

For DORIS related requests, contact Kenneth Chan in the Information Technology - division.

-
+
+
+
+
+

For DORIS related requests, contact Kenneth Chan in the Information Technology + division.

-
-
-

For an immediate ticket and faster turnaround, users and IT staff can submit their incidents - or - requests to the CityWide - Service Desk using the - Self Service Portal My Desk.

-
+
+
+
+

For an immediate ticket and faster turnaround, users and IT staff can submit their incidents + or + requests to the CityWide + Service Desk using the + Self Service Portal My Desk.

-
-
-

Get hands on tranining with MS Office as well as cybersecurity awareness resources.

-
+
+
+
+

Get hands on tranining with MS Office as well as cybersecurity awareness resources.

-
-
-
-
-

APPLICATION SUPPORT

-
- +
+
+
+
+
+

APPLICATION SUPPORT

+
+
-
-
-
-

RESOURCES

-
- + -
-
- -
-
-
-

For support on internal DORIS Applications, email the Application Development Team.

-
+
+
+
+
+

For support on internal DORIS Applications, email the Application Development Team.

-
-
-

Get instructions for accessing public wifi, outlook, and voicemail.

-
+
+
+
+

Get instructions for accessing public wifi, outlook, and voicemail.

-
-
-

For answers to common problems or issues, check out our FAQ - page.

-
+
+
+
+

For answers to common problems or issues, check out our FAQ + page.

+
-
-
-
-
-

TOOLS AND APPLICATIONS

-
- +
+
+ -
-
- +
+ -
-
-
-

NYC Security Operations Center

-
-
-

Phone: (718) 403-6761

-

Email: soc@cyber.nyc.gov

-
+
+ -
-
-
-

For quick access to commonly used DORIS applications, check out our Tools and Applications page.

-
+
+
+
+
+

For quick access to commonly used DORIS applications, check out our Tools + and Applications page.

-
-
-

Intake requests should be used to request major changes to an existing project or for - resource allocation to a new project.

-
+
+
+
+

Intake requests should be used to request major changes to an existing project or for + resource allocation to a new project.

-
-
-

Contact the NYC Security Operations Center for information about cyber - security.

+
+
+
+

Intake requests should be used to request new IT resources (storage, computers, etc.) or support for specific events

+
+
+
+ +
+
+
+
+
+
+

NYC Security Operations Center

+
+
+

Phone: (718) 403-6761

+

Email: soc@cyber.nyc.gov

+
+
+
+
+
+
+
+
+
+

Contact the NYC + Security Operations Center for information about cyber + security.

+
+
+
+
-{% endblock %} \ No newline at end of file + {% endblock %} \ No newline at end of file diff --git a/config.py b/config.py index 032d7981..29ea276f 100644 --- a/config.py +++ b/config.py @@ -31,6 +31,7 @@ class Config: USER_DATA = (os.environ.get('USER_DATA') or os.path.join(os.path.abspath(os.path.dirname(__file__)), 'data', 'users.csv')) APP_DEV_INTAKE_EMAIL_RECIPIENTS = os.environ.get('APP_DEV_INTAKE_EMAIL_RECIPIENTS', '').split(',') or [] + IT_INTAKE_EMAIL_RECIPIENTS = os.environ.get('IT_INTAKE_EMAIL_RECIPIENTS', '').split(',') or [] POSTS_PER_PAGE = 10