├── .dockerignore
├── .github
└── workflows
│ ├── build.yml
│ ├── lints.yml
│ └── tests.yml
├── .gitignore
├── LICENSE
├── README.md
├── docker-compose.override.yml
├── docker-compose.yml
├── docker
├── Dockerfile
├── clean.sh
├── dump.sh
├── init.sh
├── minio
│ └── entrypoint.sh
└── pandoc-lambda
│ ├── Dockerfile
│ ├── README.md
│ ├── entrypoint.sh
│ ├── function
│ ├── .gitignore
│ ├── README.md
│ ├── app.py
│ ├── old_pr1491
│ │ ├── reference.docx
│ │ └── table_of_contents.lua
│ ├── reference.docx
│ ├── requirements.in
│ ├── requirements.txt
│ ├── setup.cfg
│ └── table_of_contents.lua
│ └── reference_docx
│ ├── README.md
│ ├── reference.docx
│ ├── src
│ ├── [Content_Types].xml
│ ├── _rels
│ │ └── .rels
│ ├── docProps
│ │ ├── app.xml
│ │ ├── core.xml
│ │ └── custom.xml
│ └── word
│ │ ├── _rels
│ │ ├── document.xml.rels
│ │ └── footnotes.xml.rels
│ │ ├── comments.xml
│ │ ├── document.xml
│ │ ├── fontTable.xml
│ │ ├── footer_blank.xml
│ │ ├── footer_title.xml
│ │ ├── footnotes.xml
│ │ ├── header_blank.xml
│ │ ├── header_even.xml
│ │ ├── header_front_matter_even.xml
│ │ ├── header_front_matter_odd.xml
│ │ ├── header_odd.xml
│ │ ├── numbering.xml
│ │ ├── settings.xml
│ │ ├── styles.xml
│ │ ├── theme
│ │ └── theme1.xml
│ │ └── webSettings.xml
│ ├── style_structure_reference.md
│ ├── update_docx_from_xml.zsh
│ └── update_style_structure_md.py
├── package-lock.json
├── pyproject.toml
└── web
├── .gitignore
├── babel.config.js
├── codecov.yml
├── config
├── __init__.py
├── context_processors.py
├── settings
│ ├── __init__.py
│ ├── settings.py.example
│ ├── settings_base.py
│ ├── settings_dev.py
│ ├── settings_prod.py
│ └── settings_pytest.py
├── urls.py
└── wsgi.py
├── conftest.py
├── frontend
├── components
│ ├── AddContent.vue
│ ├── AnnotationBase.vue
│ ├── AnnotationExpansionToggle.vue
│ ├── AnnotationHandle.vue
│ ├── AuditButton.vue
│ ├── CaseResults.vue
│ ├── CaseSearcher.vue
│ ├── CollapseTriangle.vue
│ ├── ContextMenu.vue
│ ├── CorrectionAnnotation.vue
│ ├── Dashboard.vue
│ ├── Dashboard
│ │ └── Casebook.vue
│ ├── ElisionAnnotation.vue
│ ├── FootnoteLink.vue
│ ├── Globals.vue
│ ├── HighlightAnnotation.vue
│ ├── LegalDocumentSearch
│ │ ├── AdvancedSearch.vue
│ │ ├── LegalDocumentSearch.vue
│ │ ├── ResultsForm.vue
│ │ └── SearchForm.vue
│ ├── LinkAnnotation.vue
│ ├── LinkInput.vue
│ ├── LoadingSpinner.vue
│ ├── Modal.vue
│ ├── NoteAnnotation.vue
│ ├── PublishButton.vue
│ ├── QuickAdd.vue
│ ├── ReplacementAnnotation.vue
│ ├── SectionCloner.vue
│ ├── SideMenu.vue
│ ├── SpacePreserver.vue
│ ├── TableOfContents
│ │ ├── Entry.vue
│ │ ├── EntryAuditor.vue
│ │ ├── EntryTransmuter.vue
│ │ └── PlaceHolder.vue
│ ├── TakeNotesCloner.vue
│ ├── TheAnnotator.vue
│ ├── TheGlobalElisionExpansionButton.vue
│ ├── TheResource.vue
│ ├── TheResourceBody.vue
│ ├── TheTableOfContents.vue
│ └── TinyMCEEditor.vue
├── config
│ └── axios.js
├── directives
│ └── selectionchange.js
├── fonts
│ ├── AtlasGrotesk-Bold.eot
│ ├── AtlasGrotesk-Bold.svg
│ ├── AtlasGrotesk-Bold.ttf
│ ├── AtlasGrotesk-Bold.woff
│ ├── AtlasGrotesk-Light.eot
│ ├── AtlasGrotesk-Light.svg
│ ├── AtlasGrotesk-Light.ttf
│ ├── AtlasGrotesk-Light.woff
│ ├── AtlasGrotesk-Medium.eot
│ ├── AtlasGrotesk-Medium.svg
│ ├── AtlasGrotesk-Medium.ttf
│ ├── AtlasGrotesk-Medium.woff
│ ├── AtlasGrotesk-Regular.eot
│ ├── AtlasGrotesk-Regular.svg
│ ├── AtlasGrotesk-Regular.ttf
│ ├── AtlasGrotesk-Regular.woff
│ ├── ChronicleTextG3-Roman.eot
│ ├── ChronicleTextG3-Roman.svg
│ ├── ChronicleTextG3-Roman.ttf
│ ├── ChronicleTextG3-Roman.woff
│ ├── RobotoMono-Bold.ttf
│ ├── tinymce-small.eot
│ ├── tinymce-small.svg
│ ├── tinymce-small.ttf
│ ├── tinymce-small.woff
│ ├── tinymce.eot
│ ├── tinymce.svg
│ ├── tinymce.ttf
│ └── tinymce.woff
├── legacy
│ ├── README.txt
│ ├── lib
│ │ ├── bootstrap
│ │ │ ├── dropdown.js
│ │ │ └── modal.js
│ │ ├── helpers.js
│ │ ├── requests.js
│ │ └── ui
│ │ │ ├── component.js
│ │ │ ├── content
│ │ │ ├── annotate_modal.js
│ │ │ ├── clone_modal.js
│ │ │ ├── dashboard.js
│ │ │ ├── export_modal.js
│ │ │ ├── index.js
│ │ │ └── revise_modal.js
│ │ │ ├── filter-selectize.js
│ │ │ ├── modal.js
│ │ │ └── skip-link-focus-fix.js
│ └── polyfills
│ │ ├── index.js
│ │ └── promises.js
├── libs
│ ├── html_helpers.js
│ ├── legal_document_search.js
│ ├── resource_node_parsing.js
│ ├── text_outline_parser.js
│ ├── tinymce_extensions.js
│ └── urls.js
├── pages
│ ├── application.js
│ ├── main.scss
│ ├── rich_text_editor.js
│ ├── table_of_contents.js
│ ├── test.js
│ └── vue_app.js
├── store
│ ├── index.js
│ └── modules
│ │ ├── annotations.js
│ │ ├── annotations_ui.js
│ │ ├── case_search.js
│ │ ├── footnotes_ui.js
│ │ ├── globals.js
│ │ ├── resources_ui.js
│ │ └── table_of_contents.js
├── styles
│ ├── announcement_banner.scss
│ ├── buttons.scss
│ ├── casebook.scss
│ ├── cases.scss
│ ├── ckeditor_styles.scss
│ ├── content
│ │ ├── annotations.scss
│ │ ├── dragging.scss
│ │ └── modals.scss
│ ├── credits.scss
│ ├── dashboard.scss
│ ├── font-faces.scss
│ ├── font-vars-and-mixins.scss
│ ├── footer.scss
│ ├── forms.scss
│ ├── header.scss
│ ├── jquery.ui.custom.scss
│ ├── landing.scss
│ ├── layout.scss
│ ├── mixins.scss
│ ├── mockups.scss
│ ├── pages.scss
│ ├── searches.scss
│ ├── spinner.scss
│ ├── text.scss
│ ├── tinymce_editor.scss
│ ├── ui.scss
│ ├── uscode.scss
│ ├── users.scss
│ ├── variables.scss
│ └── vars-and-mixins.scss
└── test
│ ├── .eslintrc.js
│ ├── components
│ ├── QuickAdd.test.js
│ ├── ResultsForm.test.js
│ ├── SearchForm.test.js
│ ├── TheAnnotator.test.js
│ └── TheResourceBody.test.js
│ ├── libs
│ ├── example_tocs
│ │ ├── baker-national-security-law-2015.txt
│ │ ├── beebe-trademark-law.txt
│ │ ├── colin-miller-best-evidence.txt
│ │ ├── feldman-constitutional-law-separation-of-powers-2015.txt
│ │ ├── geier-2020.txt
│ │ ├── hatfield-ethics-tax-lawyering-3rd.txt
│ │ ├── levin-civil-procedure-pleading.txt
│ │ ├── mcfarland-park-computer-aided-civil-procedure.txt
│ │ └── verkerke-contract-doctrine.txt
│ ├── html_helpers.test.js
│ └── resource_node_parsing.test.js
│ ├── mocha_setup.js
│ └── test_helpers.js
├── main
├── __init__.py
├── admin.py
├── apps.py
├── authenticator.py
├── case_xml_converter.py
├── create_fts_index.sql
├── create_search_index.sql
├── differ.py
├── export.py
├── forms.py
├── hashers.py
├── legal_document_sources.py
├── middleware.py
├── migrations
│ ├── 0001_squashed_0072_auto_20201015_1225.py
│ ├── 0002_auto_20201021_1444.py
│ ├── 0003_alternate_legal_docs.py
│ ├── 0004_auto_20210405_1927.py
│ ├── 0005_auto_20210414_1240.py
│ ├── 0006_uscodeindex_repealed.py
│ ├── 0007_savedimage.py
│ ├── 0008_auto_20210511_1533.py
│ ├── 0009_auto_20210512_0217.py
│ ├── 0010_auto_20210512_0257.py
│ ├── 0011_auto_20210805_1723.py
│ ├── 0012_auto_20210914_0057.py
│ ├── 0013_casebookfollow.py
│ ├── 0014_auto_20211007_1248.py
│ ├── 0015_auto_20211014_0041.py
│ ├── 0016_auto_20211025_2147.py
│ ├── 0017_auto_20211130_2114.py
│ ├── 0018_auto_20211130_2236.py
│ ├── 0019_livesettings.py
│ ├── 0020_auto_20220209_1732.py
│ ├── 0021_auto_20220527_1714.py
│ ├── 0022_post_32_upgrade.py
│ ├── 0023_drop_legacy_annotation_fields.py
│ ├── 0024_drop_raw_headnote.py
│ ├── 0025_drop_created_via_import.py
│ ├── 0026_drop_public_on_textblock.py
│ ├── 0027_drop_legacy_tables.py
│ ├── 0028_fulltextsearchindex.py
│ ├── 0029_auto_20220714_1747.py
│ ├── 0030_reading_length.py
│ ├── 0031_casebook_description_20220719_1550.py
│ ├── 0032_setting_for_html_export.py
│ ├── 0033_improve_commontitle_docs.py
│ ├── 0034_add_instructional_content_field.py
│ ├── 0035_user_details.py
│ ├── 0036_instructional_material_help_text.py
│ ├── 0037_add_institution_model.py
│ ├── 0038_initial_institution_data.py
│ ├── 0039_drop_printable_livesetting.py
│ ├── 0040_drop_link_content_type_field.py
│ ├── 0041_add_casebook_tags.py
│ ├── 0042_legaldocument_indexes.py
│ ├── 0043_add_listed_publicly_field.py
│ ├── 0044_add_user_groups.py
│ ├── 0045_add_courtlistener_api_source.py
│ ├── 0046_auto_20240516_1322.py
│ ├── 0047_auto_20240516_1754.py
│ └── __init__.py
├── models.py
├── reporter.py
├── sanitize.py
├── serializers.py
├── storages.py
├── templates
│ ├── 400.html
│ ├── 403.html
│ ├── 403_csrf.html
│ ├── 404.html
│ ├── 500.html
│ ├── admin
│ │ ├── change_form_with_richeditor.html
│ │ ├── h2o_index.html
│ │ ├── input_filter.html
│ │ ├── main
│ │ │ ├── casebook
│ │ │ │ ├── change_form.html
│ │ │ │ └── inline_series.html
│ │ │ ├── legaldocument
│ │ │ │ └── change_form.html
│ │ │ ├── link
│ │ │ │ └── change_form.html
│ │ │ ├── resource
│ │ │ │ └── change_form.html
│ │ │ ├── section
│ │ │ │ └── change_form.html
│ │ │ ├── textblock
│ │ │ │ └── change_form.html
│ │ │ └── user
│ │ │ │ └── change_form.html
│ │ └── reporting
│ │ │ └── index.html
│ ├── archived_casebooks.html
│ ├── base.html
│ ├── casebook_history.html
│ ├── casebook_outline_edit.html
│ ├── casebook_page.html
│ ├── casebook_page_credits.html
│ ├── casebook_page_related.html
│ ├── casebook_page_search.html
│ ├── casebook_settings.html
│ ├── dashboard.html
│ ├── email
│ │ └── welcome.txt
│ ├── export
│ │ ├── about.html
│ │ ├── as_printable_html
│ │ │ ├── casebook.html
│ │ │ ├── credits.html
│ │ │ ├── node.html
│ │ │ ├── tbd.html
│ │ │ └── toc.html
│ │ ├── casebook.html
│ │ ├── credits.html
│ │ ├── node.html
│ │ ├── section.html
│ │ └── tbd.html
│ ├── export_error.html
│ ├── includes
│ │ ├── action_buttons.html
│ │ ├── analytics.html
│ │ ├── announcement_banner.html
│ │ ├── bodies
│ │ │ ├── case.html
│ │ │ ├── empty.html
│ │ │ ├── legal_doc.html
│ │ │ ├── link.html
│ │ │ └── text_block.html
│ │ ├── breadcrumbs.html
│ │ ├── case_header.html
│ │ ├── casebook_copyright_notice.html
│ │ ├── casebook_page_tabs.html
│ │ ├── casebook_search.html
│ │ ├── collaborators.html
│ │ ├── content_browser.html
│ │ ├── credits.html
│ │ ├── featured_casebook.html
│ │ ├── footer.html
│ │ ├── header.html
│ │ ├── headnote.html
│ │ ├── legal_doc_sources
│ │ │ ├── cap_header.html
│ │ │ ├── court_listener_header.html
│ │ │ ├── empty_header.html
│ │ │ └── gpo_header.html
│ │ ├── page_buttons.html
│ │ ├── preview_banner.html
│ │ ├── reading_mode_toc_item.html
│ │ ├── resource_tabs.html
│ │ ├── section_tabs.html
│ │ ├── sentry.html
│ │ └── table-of-contents.html
│ ├── index.html
│ ├── legal_doc.html
│ ├── pages
│ │ ├── about.html
│ │ ├── featured_casebooks.html
│ │ ├── privacy-policy.html
│ │ └── terms-of-service.html
│ ├── registration
│ │ ├── login.html
│ │ ├── password_change_done.html
│ │ ├── password_change_form.html
│ │ ├── password_reset_complete.html
│ │ ├── password_reset_confirm.html
│ │ ├── password_reset_done.html
│ │ ├── password_reset_form.html
│ │ └── sign_up.html
│ ├── resource_annotate.html
│ ├── robots.txt
│ ├── search
│ │ ├── filters.html
│ │ ├── pagination.html
│ │ ├── results.html
│ │ └── show.html
│ └── user_edit.html
├── templatetags
│ ├── call_method.py
│ ├── current_query_string.py
│ ├── export_node_html.py
│ ├── featured_casebook.py
│ ├── humanize_minutes.py
│ ├── reading_mode_toc_item.py
│ ├── short_page_range.py
│ └── string_strip.py
├── test
│ ├── __init__.py
│ ├── functional
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── fixtures
│ │ │ ├── casebooks.json
│ │ │ ├── contentannotation.json
│ │ │ ├── contentcollaborators.json
│ │ │ ├── contentnodes.json
│ │ │ ├── textblocks.json
│ │ │ └── users.json
│ │ ├── test_platform.py
│ │ └── test_reading_mode.py
│ ├── test_admin.py
│ ├── test_auth.py
│ ├── test_cloning.py
│ ├── test_credits.py
│ ├── test_drafts.py
│ ├── test_editing.py
│ ├── test_errors.py
│ ├── test_export.py
│ ├── test_forms.py
│ ├── test_permissions.py
│ ├── test_permissions_helpers.py
│ ├── test_publishing.py
│ ├── test_search.py
│ ├── test_templatetags.py
│ ├── test_user_profile.py
│ └── views.py
├── url_converters.py
├── urls.py
├── utils.py
└── views.py
├── manage.py
├── package-lock.json
├── package.json
├── reporting
├── __init__.py
├── admin
│ ├── __init__.py
│ ├── usage_dashboard.py
│ └── views.py
├── apps.py
├── create_reporting_views.py
├── matomo.py
├── migrations
│ ├── 0001_add_reporting_proxy_models.py
│ ├── 0002_plural_name_reporting.py
│ └── __init__.py
├── models.py
├── sql
│ └── reporting.sql
├── tests
│ ├── test_matomo_integration.py
│ └── test_reporting_views.py
├── urls.py
└── views.py
├── requirements.in
├── requirements.txt
├── setup.cfg
├── static
├── as_printable_html
│ ├── all.css
│ ├── as_printable_html.js
│ ├── cap.css
│ ├── pagedjs.js
│ ├── print.css
│ ├── print.js
│ ├── screen.css
│ ├── toc.css
│ └── uscode.css
├── data
│ └── ali_materials.json
├── dist
│ ├── css
│ │ ├── main.8e68f311.css
│ │ └── vue_app.62b395e0.css
│ ├── fonts
│ │ ├── AtlasGrotesk-Light.bcfd7cf6.woff
│ │ ├── AtlasGrotesk-Light.be1c731f.eot
│ │ ├── AtlasGrotesk-Light.e69872f9.ttf
│ │ ├── AtlasGrotesk-Medium.14791ebe.eot
│ │ ├── AtlasGrotesk-Medium.83bc0a62.woff
│ │ ├── AtlasGrotesk-Medium.d9cc5003.ttf
│ │ ├── AtlasGrotesk-Regular.1f499573.ttf
│ │ ├── AtlasGrotesk-Regular.94001d71.eot
│ │ ├── AtlasGrotesk-Regular.d2997fb4.woff
│ │ ├── ChronicleTextG3-Roman.6a453768.ttf
│ │ ├── ChronicleTextG3-Roman.f77c0515.woff
│ │ ├── ChronicleTextG3-Roman.f9c467a3.eot
│ │ ├── RobotoMono-Bold.c0c4a337.ttf
│ │ ├── glyphicons-halflings-regular.448c34a5.woff2
│ │ ├── glyphicons-halflings-regular.e18bbf61.ttf
│ │ ├── glyphicons-halflings-regular.f4769f9b.eot
│ │ └── glyphicons-halflings-regular.fa277232.woff
│ ├── img
│ │ ├── AtlasGrotesk-Light.219d1463.svg
│ │ ├── AtlasGrotesk-Medium.3e829f26.svg
│ │ ├── AtlasGrotesk-Regular.26133821.svg
│ │ ├── ChronicleTextG3-Roman.0e3646cf.svg
│ │ ├── Link.148d855f.svg
│ │ ├── add-casebook.ff004159.svg
│ │ ├── add-material.d109215f.svg
│ │ ├── add-section.a7b07d92.svg
│ │ ├── audit-casebook.f96e34fb.svg
│ │ ├── banner-draft-icon.514d5145.svg
│ │ ├── cancel-icon.101f571f.svg
│ │ ├── clone.fc6eb4b4.svg
│ │ ├── customize-casebook.d78e11a3.svg
│ │ ├── edit-icon.d38c8fff.svg
│ │ ├── expand-arrow.6bf1cea3.svg
│ │ ├── export-anim.c043e5fa.svg
│ │ ├── export-html.a6acb1d4.svg
│ │ ├── export.13e242d7.svg
│ │ ├── external-link-icon.3e1fd22b.svg
│ │ ├── follow.2e7d7ca7.svg
│ │ ├── glyphicons-halflings-regular.89889688.svg
│ │ ├── landing-demo.769625d8.png
│ │ ├── link-go.4122dfcb.svg
│ │ ├── lock.0ee492f3.svg
│ │ ├── logo-icon-white-on-blue.f2cf816b.svg
│ │ ├── logo.454a3835.svg
│ │ ├── next_page.13726d8a.svg
│ │ ├── prev_page.8adf0763.svg
│ │ ├── preview.a015216b.svg
│ │ ├── publish.9455f1e6.svg
│ │ ├── save-icon.82eb3293.svg
│ │ ├── search-icon.8c383980.svg
│ │ ├── take-notes-icon.26352bcb.svg
│ │ └── unfollow.54f63882.svg
│ └── js
│ │ ├── application.40739f7b.js
│ │ ├── application.40739f7b.js.map
│ │ ├── chunk-common.5c389b4d.js
│ │ ├── chunk-common.5c389b4d.js.map
│ │ ├── main.8272c3ff.js
│ │ ├── main.8272c3ff.js.map
│ │ ├── rich_text_editor.6f43d27a.js
│ │ ├── rich_text_editor.6f43d27a.js.map
│ │ ├── test.2ff30b1f.js
│ │ ├── test.2ff30b1f.js.map
│ │ ├── vue_app.d14b6f2a.js
│ │ └── vue_app.d14b6f2a.js.map
├── fonts
│ ├── AtlasGrotesk-Bold.woff2
│ ├── AtlasGrotesk-Regular.woff2
│ ├── ChronicleTextG3-Bold.woff2
│ ├── ChronicleTextG3-Italic.woff2
│ ├── ChronicleTextG3-Regular.woff2
│ ├── LibreCaslonText-Bold.woff2
│ ├── LibreCaslonText-Italic.woff2
│ ├── LibreCaslonText-Regular.woff2
│ └── OFL.txt
├── images
│ ├── .keep
│ ├── Link.svg
│ ├── add-icon.png
│ ├── arrow-search.png
│ ├── banner-draft-icon.svg
│ ├── close.png
│ ├── endorsers
│ │ ├── cohen.png
│ │ ├── fried.png
│ │ ├── fried2.png
│ │ ├── modirzadeh.png
│ │ ├── quinn.png
│ │ ├── suk-gersen.png
│ │ └── zittrain.png
│ ├── expand-arrow.svg
│ ├── external-link-icon.svg
│ ├── favicon.ico
│ ├── h20-logo.png
│ ├── icons.png
│ ├── landing-demo.png
│ ├── logo-blue-wordmark.svg
│ ├── logo-color-full-lockup.svg
│ ├── logo-color-wordmark.svg
│ ├── logo-icon-blue-on-yellow.svg
│ ├── logo-icon-white-on-blue.svg
│ ├── logo-white.png
│ ├── logo.svg
│ ├── mockups
│ │ ├── landing.png
│ │ └── new-casebook.png
│ ├── quickbar.png
│ ├── repeat_bg.png
│ ├── school-logos
│ │ ├── berkeley.png
│ │ ├── harvard.png
│ │ ├── michigan.png
│ │ ├── penn.png
│ │ ├── stanford.png
│ │ └── yale.png
│ ├── search-icon.svg
│ ├── search.png
│ ├── take-notes-icon.svg
│ ├── tinymce_icons.png
│ ├── transparent.gif
│ └── ui
│ │ ├── casebook
│ │ ├── add-casebook.svg
│ │ ├── add-material.svg
│ │ ├── add-section.svg
│ │ ├── audit-casebook.svg
│ │ ├── cancel-icon.svg
│ │ ├── clone.svg
│ │ ├── customize-casebook.svg
│ │ ├── edit-icon.svg
│ │ ├── export-anim.svg
│ │ ├── export-html.svg
│ │ ├── export.svg
│ │ ├── follow.svg
│ │ ├── link-go.svg
│ │ ├── lock.svg
│ │ ├── next_page.svg
│ │ ├── prev_page.svg
│ │ ├── preview.svg
│ │ ├── publish.svg
│ │ ├── save-icon.svg
│ │ └── unfollow.svg
│ │ ├── edit
│ │ ├── add.png
│ │ └── elide.png
│ │ ├── verified-large.png
│ │ └── verified.png
└── tinymce_skin
│ ├── README.txt
│ ├── content.css
│ ├── content.inline.css
│ ├── content.inline.min.css
│ ├── content.min.css
│ ├── content.mobile.css
│ ├── content.mobile.min.css
│ ├── fonts
│ └── tinymce-mobile.woff
│ ├── skin.css
│ ├── skin.min.css
│ ├── skin.mobile.css
│ └── skin.mobile.min.css
├── tasks.py
├── test
├── __init__.py
├── files
│ └── export
│ │ ├── export-casebook-no-annotations.docx
│ │ ├── export-casebook-no-annotations.html
│ │ ├── export-casebook-with-annotations.docx
│ │ ├── export-casebook-with-annotations.html
│ │ ├── export-resource-no-annotations.docx
│ │ ├── export-resource-no-annotations.html
│ │ ├── export-resource-with-annotations.docx
│ │ ├── export-resource-with-annotations.html
│ │ ├── export-section-no-annotations.docx
│ │ ├── export-section-no-annotations.html
│ │ ├── export-section-with-annotations.docx
│ │ └── export-section-with-annotations.html
└── test_helpers.py
├── vue.config.js
└── webpack-stats.json
/.dockerignore:
--------------------------------------------------------------------------------
1 | **
2 | !Dockerfile
3 | !web/requirements.txt
4 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build images
2 |
3 | on: workflow_dispatch
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - name: Push docker images
10 | uses: harvard-lil/docker-compose-update-action@main
11 | with:
12 | registry: "registry.lil.tools"
13 | registry-user: ${{ secrets.REPOSITORY_USER }}
14 | registry-pass: ${{ secrets.REPOSITORY_TOKEN }}
15 | bake-action: "push"
16 |
--------------------------------------------------------------------------------
/.github/workflows/lints.yml:
--------------------------------------------------------------------------------
1 | name: Lints
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | lint:
7 | runs-on: ubuntu-latest
8 |
9 | steps:
10 | - uses: actions/checkout@v4
11 | - uses: actions/setup-python@v5
12 | with:
13 | python-version: '3.11'
14 | - name: flake8
15 | run: |
16 | pip install `egrep -o 'flake8==\S+' web/requirements.txt` # install our version of flake8
17 | flake8 web/ --config web/setup.cfg
18 | flake8 docker/pandoc-lambda/function/ --config web/setup.cfg
19 |
20 | - name: black
21 | run: |
22 | pip install `egrep -o 'black==\S+' web/requirements.txt` # install our version of black
23 | black --check --diff . # Uses pyproject.toml
24 |
25 | - name: mypy
26 | run: |
27 | cd web
28 | pip install -r requirements.txt # install a full environment for mypy
29 | PYTHONPATH=. mypy
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .bundle
2 | .DS_Store
3 | .rvmrc
4 | *.swp
5 | bin/phantomjs
6 | config/initializers/h2o_secret.rb
7 | config/newrelic.yml
8 | config/skylight.yml
9 | coverage/
10 | locals/
11 | log/
12 | node_modules/
13 | public/assets/
14 | public/base.html
15 | public/cases/
16 | public/ckeditor_assets/
17 | public/collages/
18 | public/exports/
19 | public/iframe/
20 | public/index.html
21 | public/index.html.gz
22 | public/javascripts/all.js
23 | public/p/
24 | public/playlists/
25 | public/stylesheets/all.css
26 | public/svg_icons/
27 | public/system/users/images/
28 | solr/data/
29 | solr/pids/
30 | tmp/
31 | guard-sunspot/
32 | .idea
33 | /.env
34 | .tern-port
35 | /public/packs
36 | /public/packs-test
37 | yarn-debug.log*
38 | .yarn-integrity
39 | /yarn-error.log
40 | .dir-locals.el
41 | *.dump
42 | .byebug_history
43 | .tmp
44 | .venv
45 | python-shell.sh
46 |
47 | # IntelliJ IDEA
48 | *.iml
49 | .vscode
50 | .mypy_cache
51 |
52 | # Dev-only webpack stats
53 | web/webpack-stats-serve.json
54 |
55 | # Playwright output
56 | web/test-results
--------------------------------------------------------------------------------
/docker-compose.override.yml:
--------------------------------------------------------------------------------
1 | # overrides for local development, not used in CI
2 | services:
3 | pandoc-lambda:
4 | volumes:
5 | - ./docker/pandoc-lambda/function/:/function
6 | build:
7 | context: ./docker/pandoc-lambda
8 | x-bake:
9 | tags:
10 | - registry.lil.tools/harvardlil/h2o-pandoc-lambda:0.66-f4ef497565cd325e6161cb6b0dafe814
11 | platforms:
12 | - linux/amd64
13 | - linux/arm64
14 | x-hash-paths:
15 | - .
16 | web:
17 | build:
18 | context: .
19 | dockerfile: ./docker/Dockerfile
20 | x-bake:
21 | tags:
22 | - registry.lil.tools/harvardlil/h2o-python:0.117-13caa7e3bb2533fd5a1859b3854d9612
23 | platforms:
24 | - linux/amd64
25 | - linux/arm64
26 | x-hash-paths:
27 | - web/requirements.txt
28 | environment:
29 | - CAPAPI_API_KEY
30 | - GPO_API_KEY
31 | - COURTLISTENER_API_KEY
32 | - MATOMO_API_KEY
33 | - MATOMO_SITE_URL
34 |
--------------------------------------------------------------------------------
/docker/clean.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | docker compose down --remove-orphans --rmi all --volumes
4 |
--------------------------------------------------------------------------------
/docker/init.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -e
3 |
4 | display_usage() {
5 | echo "Seed dev database with data from a pg_dump file."
6 | echo
7 | echo "Usage:"
8 | echo " bash docker/init.sh -f dump_file"
9 | }
10 |
11 | # Help
12 | if [[ ( $1 == "--help") || ($1 == "-h")]]; then
13 | display_usage
14 | exit 0
15 | fi
16 |
17 |
18 | getopts ":f:" opt || true;
19 | case $opt in
20 | f)
21 | if [ -f "$OPTARG" ]
22 | then
23 | FILE=$OPTARG
24 | else
25 | echo "Invalid path."
26 | exit 1
27 | fi
28 | ;;
29 | \?)
30 | # illegal option or argument
31 | display_usage
32 | exit 1
33 | ;;
34 | :)
35 | # -f present, but no path provided
36 | echo "Please specify the path."
37 | exit 1
38 | ;;
39 | esac
40 | if [[ $((OPTIND - $#)) -ne 1 ]]; then
41 | # wrong number of args
42 | display_usage
43 | exit 1
44 | fi
45 |
46 | echo "Loading data from $FILE ..."
47 | docker cp "$FILE" "$(docker compose ps -q db)":/tmp/data.dump
48 | docker compose exec db pg_restore --username=postgres --verbose --no-owner -h localhost -d postgres /tmp/data.dump
49 | docker compose exec db rm -f /tmp/data.dump
50 |
--------------------------------------------------------------------------------
/docker/minio/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # see https://docs.docker.com/engine/reference/builder/#entrypoint
3 | set -e
4 |
5 | # Initialize a bucket for images
6 | mkdir -p "$DATA_DIR/$BUCKET"
7 |
8 | # Initialize a bucket for exports
9 | mkdir -p "$DATA_DIR/$EXPORT_BUCKET"
10 |
11 | # Initialize a bucket for PDF exports
12 | mkdir -p "$DATA_DIR/$PDF_EXPORT_BUCKET"
13 |
14 | # Pass the Docker CMD to the image's original entrypoint script.
15 | exec su -c "/usr/bin/docker-entrypoint.sh $*"
16 |
--------------------------------------------------------------------------------
/docker/pandoc-lambda/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | if [[ ${1:0:4} = 'app.' ]]; then
5 | if [ -z "${AWS_LAMBDA_RUNTIME_API}" ]; then
6 | exec watchmedo auto-restart --directory=/function --recursive --patterns="*.py" -- /usr/local/bin/aws-lambda-rie python -m awslambdaric $@
7 | else
8 | exec python -m awslambdaric $@
9 | fi
10 | else
11 | $@
12 | fi
13 |
--------------------------------------------------------------------------------
/docker/pandoc-lambda/function/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 |
--------------------------------------------------------------------------------
/docker/pandoc-lambda/function/old_pr1491/reference.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/docker/pandoc-lambda/function/old_pr1491/reference.docx
--------------------------------------------------------------------------------
/docker/pandoc-lambda/function/reference.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/docker/pandoc-lambda/function/reference.docx
--------------------------------------------------------------------------------
/docker/pandoc-lambda/function/requirements.in:
--------------------------------------------------------------------------------
1 | pip-tools
2 |
3 | awslambdaric # AWS Lambda Runtime Interface Client
4 | boto3 # S3 file transfers
5 | lxml # Word editing
6 | python-docx
7 | watchdog # Restart dev server on file changes
8 | pyyaml # Required by watchdog
9 |
--------------------------------------------------------------------------------
/docker/pandoc-lambda/function/setup.cfg:
--------------------------------------------------------------------------------
1 | ## http://flake8.pycqa.org/en/latest/user/configuration.html
2 | [flake8]
3 | ignore = E12,
4 | E2,W2,
5 | E3,W3,
6 | E4,
7 | E501,
8 | F403,F405
9 | # default ignore list via `flake8 --help`
10 | E121,E123,E126,E226,E24,E704,W503,W504
11 |
--------------------------------------------------------------------------------
/docker/pandoc-lambda/reference_docx/reference.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/docker/pandoc-lambda/reference_docx/reference.docx
--------------------------------------------------------------------------------
/docker/pandoc-lambda/reference_docx/src/_rels/.rels:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/docker/pandoc-lambda/reference_docx/src/docProps/app.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 83
4 | false
5 | false
6 | 12
7 | 12.0000
8 | false
9 | Microsoft Word 12.0.0
10 | 583
11 | Normal.dotm
12 | 0
13 | 6
14 | false
15 | 475
16 | 8
17 | 1
18 |
--------------------------------------------------------------------------------
/docker/pandoc-lambda/reference_docx/src/docProps/core.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | Title
6 | Author
7 |
8 | 2017-12-27T05:22:50Z
9 | 2017-12-27T05:22:50Z
10 |
--------------------------------------------------------------------------------
/docker/pandoc-lambda/reference_docx/src/docProps/custom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
--------------------------------------------------------------------------------
/docker/pandoc-lambda/reference_docx/src/word/_rels/footnotes.xml.rels:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docker/pandoc-lambda/reference_docx/src/word/comments.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docker/pandoc-lambda/reference_docx/src/word/footnotes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Footnote Text.
--------------------------------------------------------------------------------
/docker/pandoc-lambda/reference_docx/src/word/webSettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/docker/pandoc-lambda/reference_docx/update_docx_from_xml.zsh:
--------------------------------------------------------------------------------
1 | #!/bin/zsh
2 |
3 |
4 | echo
5 | if read -q "choice?Update working reference.docx in THIS DIRECTORY? The subsequent two commands will use non-updated data if you don't: [y/N] (don't press return after)"; then
6 | rm ./reference.docx
7 | cd src || exit
8 | zip -r ../reference.docx * || cd - || exit
9 | cd - || exit
10 | else
11 | echo "Ok, Not Updating."
12 | fi
13 |
14 | echo
15 | if read -q "choice?Update Markdown Style Document? : [y/N] (don't press return after)"; then
16 | echo; echo "Updating..."
17 | python3 update_style_structure_md.py reference.docx style_structure_reference.md
18 | else
19 | echo "Ok, Not Updating."
20 | fi
21 |
22 | echo
23 | if read -q "choice?Overwrite the deployed reference.docx in the LAMBDA FUNCTION DIRECTORY with the version in THIS DIRECTORY? : "; then
24 | echo; echo "Overwriting version in code directory..."
25 | cp reference.docx ../function/
26 | else
27 | echo "Ok, Not Replacing"
28 | fi
29 | echo
30 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.black]
2 | line-length = 100
3 | extend-exclude = "web/.*/migrations/"
4 | include = "web/.*.py$"
5 |
--------------------------------------------------------------------------------
/web/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | *.sqlite3
3 | .python-version
4 | settings.py
5 |
--------------------------------------------------------------------------------
/web/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | ['@vue/app']
4 | ]
5 | }
--------------------------------------------------------------------------------
/web/codecov.yml:
--------------------------------------------------------------------------------
1 | # don't fail the PR on coverage decrease
2 | coverage:
3 | status:
4 | project: false
5 | patch: false
6 | changes: false
7 |
8 | # rewrite paths so "main/models.py" becomes "_python/main/models.py" in coverage file:
9 | fixes:
10 | - "::_python/"
11 |
--------------------------------------------------------------------------------
/web/config/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/config/__init__.py
--------------------------------------------------------------------------------
/web/config/settings/settings.py.example:
--------------------------------------------------------------------------------
1 | # This file is necessary only if you want to modify default settings from settings_dev.py.
2 | # To use, copy to settings.py:
3 |
4 | from .settings_dev import * # noqa
5 |
6 |
7 | # log all SQL statements:
8 | # LOGGING['loggers']['django.db.backends'] = {
9 | # 'level': 'DEBUG',
10 | # 'handlers': ['console'],
11 | # 'propagate': False,
12 | # }
13 |
14 | # If you want to load case text from CAP:
15 | CAPAPI_API_KEY = ''
--------------------------------------------------------------------------------
/web/config/settings/settings_prod.py:
--------------------------------------------------------------------------------
1 | from .settings_base import * # noqa
2 |
3 | DEBUG = False
4 |
5 | SESSION_COOKIE_SECURE = True
6 | CSRF_COOKIE_SECURE = True
7 |
8 | # logging
9 | LOGGING["loggers"] = {
10 | "django": {
11 | "handlers": ["file", "mail_admins"],
12 | "level": "INFO",
13 | "propagate": True,
14 | },
15 | "django.request": {
16 | "handlers": ["mail_admins"],
17 | "level": "ERROR",
18 | "propagate": False,
19 | },
20 | }
21 |
--------------------------------------------------------------------------------
/web/config/settings/settings_pytest.py:
--------------------------------------------------------------------------------
1 | # Django settings used by pytest
2 |
3 | # WARNING: this imports from .settings_dev instead of config.settings, meaning it chooses to IGNORE any settings in
4 | # config/settings/settings.py. This is potentially better (in that it doesn't return different results locally than
5 | # it will on CI), but also potentially worse (in that you can't try out settings tweaks in settings.py and run tests
6 | # against them).
7 |
8 | from .settings_dev import *
9 |
10 | TESTING = True
11 |
12 | # Don't use whitenoise for tests. Including whitenoise causes it to rescan static during each test, which greatly
13 | # increases test time.
14 | MIDDLEWARE.remove("whitenoise.middleware.WhiteNoiseMiddleware")
15 |
16 | CAPAPI_API_KEY = "12345"
17 |
18 | LOGGING["loggers"]["django"]["handlers"].append("mail_admins")
19 |
20 | COVER_IMAGES = True
21 | CELERY_TASK_ALWAYS_EAGER = False
22 |
--------------------------------------------------------------------------------
/web/config/urls.py:
--------------------------------------------------------------------------------
1 | """config URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/2.2/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: path('', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.urls import include, path
14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
15 | """
16 |
17 | from django.conf import settings
18 | from django.urls import path, include
19 | from main.admin import admin_site # type: ignore # main/admin.py is entirely ignored
20 |
21 |
22 | handler400 = "main.views.bad_request"
23 | handler500 = "main.views.server_error"
24 |
25 | urlpatterns = [
26 | path("", include("main.urls")),
27 | path("django-admin/", admin_site.urls),
28 | ]
29 |
30 | # use django-debug-toolbar if installed
31 | if settings.DEBUG:
32 | try:
33 | import debug_toolbar
34 |
35 | urlpatterns += [path("__debug__/", include(debug_toolbar.urls))]
36 | except ImportError:
37 | pass
38 |
--------------------------------------------------------------------------------
/web/config/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for config project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
15 |
16 | # patch email sending to retry on error, to work around sporadic connection issues
17 | from django.core.mail import EmailMessage
18 | from smtplib import SMTPException
19 | from time import sleep
20 |
21 | _orig_send = EmailMessage.send
22 |
23 |
24 | def retrying_send(message, *args, **kwargs):
25 | try:
26 | return _orig_send(message, *args, **kwargs)
27 | except (SMTPException, TimeoutError):
28 | sleep(1)
29 | return _orig_send(message, *args, **kwargs)
30 |
31 |
32 | # Mypy doesn't like monkey patching https://github.com/python/mypy/issues/2427
33 | EmailMessage.send = retrying_send # type: ignore
34 |
35 | application = get_wsgi_application()
36 |
--------------------------------------------------------------------------------
/web/frontend/components/CollapseTriangle.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
13 |
14 |
36 |
--------------------------------------------------------------------------------
/web/frontend/components/Globals.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
21 |
--------------------------------------------------------------------------------
/web/frontend/components/HighlightAnnotation.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 | Remove highlight
8 |
9 |
10 |
11 |
12 |
13 |
20 |
21 |
33 |
--------------------------------------------------------------------------------
/web/frontend/components/LinkInput.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
16 |
17 |
19 |
--------------------------------------------------------------------------------
/web/frontend/components/LoadingSpinner.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/web/frontend/components/SideMenu.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
13 |
14 |
27 |
--------------------------------------------------------------------------------
/web/frontend/components/SpacePreserver.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
20 |
--------------------------------------------------------------------------------
/web/frontend/components/TableOfContents/PlaceHolder.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | This {{nodeType}} is empty.
6 |
7 |
8 |
9 |
10 |
11 |
16 |
--------------------------------------------------------------------------------
/web/frontend/components/TheGlobalElisionExpansionButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Hide
4 | Show
5 | elided text
6 |
7 |
8 |
9 |
23 |
24 |
41 |
--------------------------------------------------------------------------------
/web/frontend/components/TinyMCEEditor.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
29 |
--------------------------------------------------------------------------------
/web/frontend/config/axios.js:
--------------------------------------------------------------------------------
1 | import AxiosConfig from "axios";
2 | import {get_csrf_token} from 'legacy/lib/helpers';
3 |
4 | let headers = {"Content-Type": "application/json",
5 | "Accept": "application/json"};
6 | const csrf_token = get_csrf_token();
7 | if(csrf_token) headers["X-CSRF-Token"] = csrf_token;
8 |
9 | const Axios = AxiosConfig.create({headers: headers});
10 |
11 | // Add method override to request
12 | Axios.interceptors.request.use(config => {
13 | const method = config.method.toUpperCase();
14 | if (["PUT", "DELETE", "PATCH"].includes(method)) {
15 | config.headers = {
16 | ...config.headers,
17 | ["X-HTTP-Method-Override"]: method,
18 | };
19 | config.method = "post";
20 | }
21 | return config;
22 | });
23 |
24 | export default Axios;
25 |
--------------------------------------------------------------------------------
/web/frontend/directives/selectionchange.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 |
3 | const handlers = new WeakMap();
4 | const prev_in_el = new WeakMap();
5 |
6 | // Accepts a handler in the form of function(event, selection)
7 | // where selection will be null in the event the user has selected
8 | // something outside of the element on which this directive has been placed.
9 | Vue.directive('selectionchange', {
10 | inserted(el, binding) {
11 | handlers.set(el, function (evt) {
12 | const sel = document.getSelection();
13 | const in_el = el.contains(sel.anchorNode);
14 | const lost_focus = prev_in_el.get(el) && !in_el;
15 | if (in_el || lost_focus) {
16 | binding.value(evt, lost_focus ? null : sel);
17 | }
18 | prev_in_el.set(el, in_el);
19 | });
20 | document.addEventListener('selectionchange', handlers.get(el));
21 | },
22 | unbind(el, binding) {
23 | document.removeEventListener('selectionchange', handlers.get(el));
24 | handlers.delete(el);
25 | prev_in_el.delete(el);
26 | }
27 | });
28 |
--------------------------------------------------------------------------------
/web/frontend/fonts/AtlasGrotesk-Bold.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/AtlasGrotesk-Bold.eot
--------------------------------------------------------------------------------
/web/frontend/fonts/AtlasGrotesk-Bold.svg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/AtlasGrotesk-Bold.svg
--------------------------------------------------------------------------------
/web/frontend/fonts/AtlasGrotesk-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/AtlasGrotesk-Bold.ttf
--------------------------------------------------------------------------------
/web/frontend/fonts/AtlasGrotesk-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/AtlasGrotesk-Bold.woff
--------------------------------------------------------------------------------
/web/frontend/fonts/AtlasGrotesk-Light.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/AtlasGrotesk-Light.eot
--------------------------------------------------------------------------------
/web/frontend/fonts/AtlasGrotesk-Light.svg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/AtlasGrotesk-Light.svg
--------------------------------------------------------------------------------
/web/frontend/fonts/AtlasGrotesk-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/AtlasGrotesk-Light.ttf
--------------------------------------------------------------------------------
/web/frontend/fonts/AtlasGrotesk-Light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/AtlasGrotesk-Light.woff
--------------------------------------------------------------------------------
/web/frontend/fonts/AtlasGrotesk-Medium.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/AtlasGrotesk-Medium.eot
--------------------------------------------------------------------------------
/web/frontend/fonts/AtlasGrotesk-Medium.svg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/AtlasGrotesk-Medium.svg
--------------------------------------------------------------------------------
/web/frontend/fonts/AtlasGrotesk-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/AtlasGrotesk-Medium.ttf
--------------------------------------------------------------------------------
/web/frontend/fonts/AtlasGrotesk-Medium.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/AtlasGrotesk-Medium.woff
--------------------------------------------------------------------------------
/web/frontend/fonts/AtlasGrotesk-Regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/AtlasGrotesk-Regular.eot
--------------------------------------------------------------------------------
/web/frontend/fonts/AtlasGrotesk-Regular.svg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/AtlasGrotesk-Regular.svg
--------------------------------------------------------------------------------
/web/frontend/fonts/AtlasGrotesk-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/AtlasGrotesk-Regular.ttf
--------------------------------------------------------------------------------
/web/frontend/fonts/AtlasGrotesk-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/AtlasGrotesk-Regular.woff
--------------------------------------------------------------------------------
/web/frontend/fonts/ChronicleTextG3-Roman.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/ChronicleTextG3-Roman.eot
--------------------------------------------------------------------------------
/web/frontend/fonts/ChronicleTextG3-Roman.svg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/ChronicleTextG3-Roman.svg
--------------------------------------------------------------------------------
/web/frontend/fonts/ChronicleTextG3-Roman.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/ChronicleTextG3-Roman.ttf
--------------------------------------------------------------------------------
/web/frontend/fonts/ChronicleTextG3-Roman.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/ChronicleTextG3-Roman.woff
--------------------------------------------------------------------------------
/web/frontend/fonts/RobotoMono-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/RobotoMono-Bold.ttf
--------------------------------------------------------------------------------
/web/frontend/fonts/tinymce-small.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/tinymce-small.eot
--------------------------------------------------------------------------------
/web/frontend/fonts/tinymce-small.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/tinymce-small.ttf
--------------------------------------------------------------------------------
/web/frontend/fonts/tinymce-small.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/tinymce-small.woff
--------------------------------------------------------------------------------
/web/frontend/fonts/tinymce.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/tinymce.eot
--------------------------------------------------------------------------------
/web/frontend/fonts/tinymce.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/tinymce.ttf
--------------------------------------------------------------------------------
/web/frontend/fonts/tinymce.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/frontend/fonts/tinymce.woff
--------------------------------------------------------------------------------
/web/frontend/legacy/README.txt:
--------------------------------------------------------------------------------
1 | These files were originally served using an earlier Rails asset pipeline (Sprockets) instead of the later Rails asset
2 | pipeline (webpacker). They could be better integrated with the other frontend files.
--------------------------------------------------------------------------------
/web/frontend/legacy/lib/ui/content/annotate_modal.js:
--------------------------------------------------------------------------------
1 | import ModalComponent from 'legacy/lib/ui/modal';
2 | import delegate from 'delegate';
3 | import {html} from 'es6-string-html-template';
4 |
5 | delegate(document, '.annotate-casebook', 'click', showAnnotateModal);
6 |
7 | function showAnnotateModal (e) {
8 | new AnnotateModal('clone-modal', e.target, {});
9 |
10 | e.currentTarget.activeElement.dataset.processing = "true" // override the component destroy events
11 | }
12 |
13 | class AnnotateModal extends ModalComponent {
14 | template () {
15 | return html`
16 |
17 |
18 |
19 |
20 |
You can view this copy on your Dashboard
21 |
22 |
27 |
28 |
29 |
30 |
`
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/web/frontend/legacy/lib/ui/content/clone_modal.js:
--------------------------------------------------------------------------------
1 | import ModalComponent from 'legacy/lib/ui/modal';
2 | import delegate from 'delegate';
3 | import {html} from 'es6-string-html-template';
4 |
5 | delegate(document, '.clone-casebook', 'click', showCloneModal);
6 |
7 | function showCloneModal (e) {
8 | new CloneModal('clone-modal', e.target, {});
9 |
10 | e.currentTarget.activeElement.dataset.processing = "true" // override the component destroy events
11 | }
12 |
13 | class CloneModal extends ModalComponent {
14 | template () {
15 | return html``
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/web/frontend/legacy/lib/ui/content/index.js:
--------------------------------------------------------------------------------
1 | import './dashboard.js';
2 | import './revise_modal';
3 | import './clone_modal';
4 | import './annotate_modal';
5 | import './export_modal.js';
6 |
--------------------------------------------------------------------------------
/web/frontend/legacy/lib/ui/content/revise_modal.js:
--------------------------------------------------------------------------------
1 | import {html} from 'es6-string-html-template';
2 | import delegate from 'delegate';
3 | import ModalComponent from 'legacy/lib/ui/modal';
4 |
5 | delegate(document, '.create-draft', 'click', showReviseModal);
6 |
7 | function showReviseModal (e) {
8 | new ReviseModal('revise-modal', e.target, {});
9 |
10 | e.currentTarget.activeElement.dataset.processing = "true" // override the component destroy events
11 | }
12 |
13 | class ReviseModal extends ModalComponent {
14 |
15 | template () {
16 | return html`
17 |
18 |
19 |
20 |
21 |
Your casebook will remain published and you can merge in any changes when you're ready.
22 |
27 |
28 |
29 |
30 |
`
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/web/frontend/legacy/lib/ui/filter-selectize.js:
--------------------------------------------------------------------------------
1 | $(document).ready(e => {
2 |
3 | if($('.view-searches-index, .view-searches-show').length){
4 |
5 | $('#search_author').on('change', function (e) {
6 | e.target.closest('form').submit();
7 | });
8 |
9 | $('#search_school').on('change', function (e) {
10 | e.target.closest('form').submit();
11 | });
12 |
13 | $('#search_sort').on('change', function (e) {
14 | e.target.closest('form').submit();
15 | });
16 | }
17 |
18 | });
19 |
--------------------------------------------------------------------------------
/web/frontend/legacy/lib/ui/skip-link-focus-fix.js:
--------------------------------------------------------------------------------
1 | /**
2 | * File skip-link-focus-fix.js.
3 | *
4 | * Helps with accessibility for keyboard only users.
5 | *
6 | * Learn more: https://git.io/vWdr2
7 | */
8 | ( function() {
9 | var isIe = /(trident|msie)/i.test( navigator.userAgent );
10 |
11 | if ( isIe && document.getElementById && window.addEventListener ) {
12 | window.addEventListener( 'hashchange', function() {
13 | var id = location.hash.substring( 1 ),
14 | element;
15 |
16 | if ( ! ( /^[A-z0-9_-]+$/.test( id ) ) ) {
17 | return;
18 | }
19 |
20 | element = document.getElementById( id );
21 |
22 | if ( element ) {
23 | if ( ! ( /^(?:a|select|input|button|textarea)$/i.test( element.tagName ) ) ) {
24 | element.tabIndex = -1;
25 | }
26 |
27 | element.focus();
28 | }
29 | }, false );
30 | }
31 | } )();
--------------------------------------------------------------------------------
/web/frontend/legacy/polyfills/index.js:
--------------------------------------------------------------------------------
1 | import "@babel/polyfill";
2 | import 'element-closest';
3 | import './promises';
4 |
--------------------------------------------------------------------------------
/web/frontend/legacy/polyfills/promises.js:
--------------------------------------------------------------------------------
1 | import Promise from 'bluebird';
2 | window.Promise = Promise;
3 | Promise.config({
4 | longStackTraces: true,
5 | warnings: true
6 | });
7 |
--------------------------------------------------------------------------------
/web/frontend/pages/application.js:
--------------------------------------------------------------------------------
1 | // This file is compiled by vue-cli as configured by vue.config.js, as are other files in this directory.
2 | // NOTE: jquery itself is imported using ProvidePlugin
3 | import 'jquery-ui';
4 | import 'jquery-ujs';
5 | import 'legacy/polyfills';
6 | import 'legacy/lib/bootstrap/dropdown';
7 | import 'legacy/lib/helpers';
8 | import 'legacy/lib/requests';
9 | import 'legacy/lib/ui/skip-link-focus-fix';
10 | import 'legacy/lib/ui/content';
11 | import 'legacy/lib/ui/filter-selectize';
12 |
--------------------------------------------------------------------------------
/web/frontend/pages/main.scss:
--------------------------------------------------------------------------------
1 | @import 'variables';
2 |
3 | // import bootstrap mixins to build semantic styles
4 | @import '~bootstrap-sass/assets/stylesheets/bootstrap';
5 | @import "~bootstrap-sass/assets/stylesheets/bootstrap/scaffolding";
6 | @import "~bootstrap-sass/assets/stylesheets/bootstrap/normalize";
7 |
8 | // shared variables
9 | @import 'font-vars-and-mixins';
10 | @import 'font-faces';
11 | @import 'mixins';
12 |
13 | // global styles
14 | @import 'text';
15 | @import 'forms';
16 | @import 'buttons';
17 |
18 | // shared semantic styles
19 | @import 'layout';
20 | @import 'header';
21 | @import 'footer';
22 | @import 'mockups';
23 | @import 'spinner';
24 | @import 'announcement_banner';
25 |
26 | // view semantic styles
27 | @import 'landing';
28 | @import 'searches';
29 | @import 'users';
30 | @import 'dashboard';
31 | @import 'casebook';
32 | @import 'pages';
33 | @import 'cases';
34 | @import 'uscode';
35 | @import 'tinymce_editor';
36 |
--------------------------------------------------------------------------------
/web/frontend/pages/rich_text_editor.js:
--------------------------------------------------------------------------------
1 | import tinymce from 'tinymce/tinymce';
2 | window.tinymce = tinymce;
3 | import 'tinymce/themes/silver';
4 | import 'tinymce/icons/default';
5 | import 'tinymce/plugins/link';
6 | import 'tinymce/plugins/lists';
7 | import 'tinymce/plugins/image';
8 | import 'tinymce/plugins/table';
9 | import 'tinymce/plugins/code';
10 | import 'tinymce/plugins/paste';
11 | import 'tinymce/plugins/media';
12 | import 'tinymce/plugins/noneditable';
13 | import {getInitConfig} from '../libs/tinymce_extensions';
14 |
15 | const ENHANCED = window.ENABLE_MEDIA_UPLOAD;
16 |
17 | function initRichTextEditor(element, code=false) {
18 | // Vue rebuilds the whole dom in between the call to init and tinymce actually doing the init
19 | // so we use a selector here until we use vue to init tinymce
20 | const selector=`${element.type}#${element.id}`;
21 | let config = getInitConfig(selector, ENHANCED, code);
22 |
23 | return tinymce.init(config);
24 | }
25 |
26 | for (const textArea of document.querySelectorAll('.richtext-editor'))
27 | initRichTextEditor(textArea);
28 |
29 | for (const textArea of document.querySelectorAll('.richtext-editor-src'))
30 | initRichTextEditor(textArea, true);
31 |
32 | global.initRichTextEditor = initRichTextEditor;
33 |
--------------------------------------------------------------------------------
/web/frontend/pages/table_of_contents.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import VueRouter from 'vue-router';
3 | Vue.use(VueRouter);
4 | Vue.config.productionTip = process.env.NODE_ENV == "development";
5 |
6 | import store from "../store/index";
7 | import "../config/axios";
8 |
9 | import TheTableOfContents from "../components/TheTableOfContents";
10 |
11 | document.addEventListener("DOMContentLoaded", () => {
12 | const routes = [
13 | { path: '/casebooks/:casebook_id/section/:section_id/', component: TheTableOfContents },
14 | { path: '/casebooks/:casebook_id/resource/:section_id/', component: TheTableOfContents },
15 | { path: '/casebooks/:casebook_id/', component: TheTableOfContents }
16 |
17 | ];
18 |
19 | const router = new VueRouter({
20 | routes,
21 | scrollBehavior: function(to, from, savedPosition) {
22 | if (to.hash) {
23 | return {selector: to.hash};
24 | } else {
25 | return { x: 0, y: 0 };
26 | }
27 | },
28 | mode: 'history'
29 | });
30 |
31 | const el = document.getElementById("table-of-contents");
32 | const app = new Vue({
33 | el,
34 | store,
35 | router,
36 | components: {
37 | TheTableOfContents
38 | }
39 | });
40 |
41 | window.app = app;
42 | });
43 |
--------------------------------------------------------------------------------
/web/frontend/pages/test.js:
--------------------------------------------------------------------------------
1 | import dragMock from 'drag-mock';
2 | window.dragMock = dragMock;
3 |
--------------------------------------------------------------------------------
/web/frontend/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import Vuex from "vuex";
3 | import annotations from "./modules/annotations";
4 | import annotations_ui from "./modules/annotations_ui";
5 | import footnotes_ui from "./modules/footnotes_ui";
6 | import resources_ui from "./modules/resources_ui";
7 | import table_of_contents from "./modules/table_of_contents";
8 | import createLogger from "vuex/dist/logger";
9 | import case_search from './modules/case_search';
10 | import globals from './modules/globals';
11 |
12 | Vue.use(Vuex);
13 |
14 | const debug = process.env.NODE_ENV == "development";
15 |
16 | export default new Vuex.Store({
17 | modules: {
18 | annotations,
19 | annotations_ui,
20 | footnotes_ui,
21 | resources_ui,
22 | table_of_contents,
23 | case_search,
24 | globals
25 | },
26 | strict: debug,
27 | plugins: debug ? [createLogger()] : []
28 | });
29 |
--------------------------------------------------------------------------------
/web/frontend/store/modules/footnotes_ui.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 |
3 | const state = {
4 | all: {}
5 | };
6 |
7 | const getters = {
8 | getById: state => id =>
9 | state.all[id]
10 | };
11 |
12 | const mutations = {
13 | register: (state, payload) =>
14 | Vue.set(state.all, payload.id, payload.annotationIds)
15 | };
16 |
17 | export default {
18 | namespaced: true,
19 | state,
20 | getters,
21 | mutations
22 | };
23 |
--------------------------------------------------------------------------------
/web/frontend/store/modules/globals.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | const state = {
4 | casebook:null,
5 | section:null,
6 | inAuditMode: false
7 | };
8 |
9 | const getters = {
10 | casebook: (state) => () => {
11 | return state.casebook;
12 | },
13 | section: (state) => () => {
14 | return state.section;
15 | },
16 | inAuditMode: (state) => () => {
17 | return state.inAuditMode;
18 | },
19 | };
20 |
21 | const mutations = {
22 | setCasebook: (state,value) => state.casebook = value,
23 | setSection: (state,value) => state.section = value,
24 | setAuditMode: (state,value) => state.inAuditMode = value
25 | };
26 |
27 | export default {
28 | namespaced: true,
29 | state,
30 | getters,
31 | mutations
32 | };
33 |
--------------------------------------------------------------------------------
/web/frontend/store/modules/resources_ui.js:
--------------------------------------------------------------------------------
1 | const state = {
2 | editable: false
3 | };
4 |
5 | const getters = {
6 | getEditability: state =>
7 | state.editable
8 | };
9 |
10 | const mutations = {
11 | setEditability: (state, payload) =>
12 | state.editable = payload
13 | };
14 |
15 | export default {
16 | namespaced: true,
17 | state,
18 | getters,
19 | mutations
20 | };
21 |
--------------------------------------------------------------------------------
/web/frontend/styles/announcement_banner.scss:
--------------------------------------------------------------------------------
1 | body {
2 | .announcement-banner {
3 | background-color: $tinted-light-blue;
4 | font-size: 14px;
5 | padding: 10px 15px;
6 | @include make-row();
7 |
8 | p {
9 | margin: 0;
10 | a {
11 | color: inherit;
12 | }
13 | a.standout-link {
14 | color: $light-blue;
15 | }
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/web/frontend/styles/buttons.scss:
--------------------------------------------------------------------------------
1 | @mixin btn()
2 | {
3 | @include sans-serif($regular, 13px, 16px);
4 | padding: 14px 18px 12px;
5 | border-width: 0px;
6 | text-decoration: none;
7 | box-shadow: none;
8 | }
9 |
10 | @mixin btn-white()
11 | {
12 | @include button-variant($black, $white, transparent);
13 | font-weight: $bold;
14 | }
15 |
16 | .simple_form input[type=submit] {
17 | @extend .btn;
18 | @extend .btn-primary;
19 | }
20 |
21 | .button_to input[type=submit] {
22 | border: none;
23 | padding: 0;
24 | text-align: left;
25 | }
26 |
27 | a.reset-button {
28 | @extend .btn;
29 | @extend .btn-warning;
30 | }
31 |
--------------------------------------------------------------------------------
/web/frontend/styles/cases.scss:
--------------------------------------------------------------------------------
1 | @import 'font-vars-and-mixins';
2 |
3 | .legal-doc-header {
4 | background-color: $white;
5 | padding: 24px 32px 10px 32px;
6 | @include serif-text($bold, 14px, 18px);
7 | line-height:unset;
8 | }
9 |
10 | .case-header {
11 | background-color: white;
12 |
13 | .title,
14 | .citation,
15 | .decisiondate,
16 | .docketnumber,
17 | .court {
18 | text-align:center;
19 | }
20 | .citation,
21 | .decisiondate,
22 | .docketnumber,
23 | .court {
24 | letter-spacing: 1px;
25 | padding: 4px;
26 | }
27 | .court {
28 | font-size:24px;
29 | }
30 | .citation,
31 | .decisiondate,
32 | .docketnumber {
33 | font-size: 18px;
34 | }
35 | .title {
36 | @include serif-text($bold, 27px, 33px);
37 | padding: 20px 4px 10px 4px;
38 | }
39 | }
40 |
41 | .case-text {
42 | .parties,
43 | .decisiondate,
44 | .docketnumber,
45 | .citations,
46 | .syllabus,
47 | .synopsis,
48 | .court {
49 | display:none;
50 | }
51 | .page-label {
52 | display: none;
53 | }
54 |
55 | aside.footnote > a {
56 | float: left;
57 | }
58 | img {
59 | max-width: 100%;
60 | width: auto;
61 | height: auto;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/web/frontend/styles/ckeditor_styles.scss:
--------------------------------------------------------------------------------
1 | .ck-editor__editable {
2 | height: 200px;
3 | }
--------------------------------------------------------------------------------
/web/frontend/styles/font-faces.scss:
--------------------------------------------------------------------------------
1 | @font-face {
2 | @include load-font($preferred-sans-serif, normal, $bold, '~@/fonts/AtlasGrotesk-Medium');
3 | }
4 |
5 | @font-face {
6 | @include load-font($preferred-sans-serif, normal, $regular, '~@/fonts/AtlasGrotesk-Regular');
7 | }
8 |
9 | @font-face {
10 | @include load-font($preferred-sans-serif, normal, $light, '~@/fonts/AtlasGrotesk-Light');
11 | }
12 |
13 |
14 | @font-face {
15 | @include load-font($preferred-serif, normal, $regular, '~@/fonts/ChronicleTextG3-Roman');
16 | }
17 |
18 | @font-face {
19 | font-family: $preferred-mono;
20 | font-style: normal;
21 | font-weight: $regular;
22 | src: url('~@/fonts/RobotoMono-Bold.ttf') format('ttf');
23 | }
24 |
--------------------------------------------------------------------------------
/web/frontend/styles/font-vars-and-mixins.scss:
--------------------------------------------------------------------------------
1 | $bold: 700;
2 | $medium: 500;
3 | $regular: 400;
4 | $light: 300;
5 |
6 | @mixin load-font($family, $style, $weight, $path)
7 | {
8 | font-family: $family;
9 | font-style: $style;
10 | font-weight: $weight;
11 | src: url($path + '.eot');
12 | src: url($path + '.eot') format('embedded-opentype'),
13 | url($path + '.woff') format('woff'),
14 | url($path + '.ttf') format('ttf'),
15 | url($path + '.svg') format('svg');
16 | }
17 |
18 | @mixin set-font($family, $weight, $size, $height)
19 | {
20 | font-family: $family;
21 | font-weight: $weight;
22 | font-size: $size;
23 | line-height: $height;
24 | }
25 |
26 | @mixin sans-serif($weight, $size, $height)
27 | {
28 | @include set-font($font-family-sans-serif, $weight, $size, $height);
29 | }
30 |
31 | @mixin serif-headline($weight, $size, $height)
32 | {
33 | @include set-font($font-family-headline, $weight, $size, $height);
34 | }
35 |
36 | @mixin serif-text($weight, $size, $height)
37 | {
38 | @include set-font($font-family-serif, $weight, $size, $height);
39 | }
40 |
41 | @mixin monospace($weight, $size, $height)
42 | {
43 | @include set-font($font-family-monospace, $weight, $size, $height);
44 | }
45 |
--------------------------------------------------------------------------------
/web/frontend/styles/footer.scss:
--------------------------------------------------------------------------------
1 | main > section:last-child {
2 | padding-bottom: 105px;
3 | }
4 | body {
5 | #main-footer {
6 | @include make-row();
7 | padding: 40px 0 50px;
8 |
9 | background-color: $light-blue;
10 | color: white;
11 |
12 | a {
13 | @include sans-serif($bold, 14px, 20px);
14 | @include link-color($white);
15 | }
16 | .link {
17 | display: block;
18 | }
19 | .content {
20 | @extend .container;
21 | }
22 | .layout {
23 | @include make-row();
24 | }
25 | .brand {
26 | background-image: url('~static/images/logo-white.png');
27 | background-position: left;
28 | height: 26px;
29 | margin: 10px 0;
30 | color: transparent;
31 | }
32 | .link-group {
33 | &:first-child {
34 | @include make-md-column(2);
35 | }
36 | &:nth-child(2) {
37 | @include make-md-column(3);
38 | }
39 | }
40 | .info {
41 | @include make-md-column(3);
42 | @extend .pull-right;
43 | text-align: right;
44 | .copyright {
45 | margin-top: 15px;
46 | @include sans-serif($bold, 10px, 16px);
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/web/frontend/styles/mockups.scss:
--------------------------------------------------------------------------------
1 | body > .overlay {
2 | display: none;
3 | position: absolute;
4 | left: 0;
5 | top: 0;
6 | right: 0;
7 | height: 3000px;
8 | background-position: center 0;
9 | background-size: initial;
10 | opacity: 0.3;
11 | z-index: 10;
12 | pointer-events: none;
13 | }
14 | body.view-base-index > .overlay {
15 | // background-image: url('~static/images/mockups/landing.png');
16 | display: none;
17 | }
18 |
19 | body.view-playlists-edit > .overlay {
20 | // background-image: url('~static/images/mockups/new-casebook.png');
21 | }
22 |
--------------------------------------------------------------------------------
/web/frontend/styles/tinymce_editor.scss:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/web/frontend/styles/uscode.scss:
--------------------------------------------------------------------------------
1 | @import 'font-vars-and-mixins';
2 |
3 | // US GPO
4 |
5 | h4.subsection-head {
6 | font-weight: bold;
7 | font-size: 22px;
8 | }
9 |
10 | h4.notes-section {
11 | margin-top: 6rem;
12 | font-weight: bold;
13 | font-size: 22px;
14 | }
15 |
16 | h4.paragraph-head {
17 | padding-left: 1rem;
18 | }
19 |
20 | @for $indent from 1 through 5 {
21 | // code to execute on each loop
22 | p.statutory-body-#{$indent}em {
23 | padding-left: #{1+$indent}rem;
24 | }
25 | }
26 |
27 | // Headers
28 |
29 | header.uscode-header {
30 | text-align: center;
31 | font-size: 18px;
32 | font-weight: bold;
33 | .citation {
34 | font-size: 18px;
35 | }
36 | .title {
37 | @include serif-text($bold, 27px, 33px);
38 | }
39 | }
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/web/frontend/styles/vars-and-mixins.scss:
--------------------------------------------------------------------------------
1 | @import 'styles/variables';
2 | @import '~bootstrap-sass/assets/stylesheets/bootstrap/variables';
3 | @import '~bootstrap-sass/assets/stylesheets/bootstrap/mixins';
4 | @import 'styles/font-vars-and-mixins';
5 | @import 'styles/mixins';
6 |
--------------------------------------------------------------------------------
/web/frontend/test/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | mocha: true
4 | }
5 | }
--------------------------------------------------------------------------------
/web/frontend/test/components/TheAnnotator.test.js:
--------------------------------------------------------------------------------
1 | import { parseHTML } from '../test_helpers';
2 |
3 | import { mount } from '@vue/test-utils';
4 | import TheAnnotator from '@/components/TheAnnotator';
5 |
6 | describe('TheAnnotator', () => {
7 |
8 | describe('contributesToOffsets', () => {
9 | it('returns false when node has the data-exclude-from-offset-calcs property', () => {
10 | const wrapper = mount(TheAnnotator);
11 | const node = parseHTML('foo
');
12 | expect(wrapper.vm.contributesToOffsets(node)).toBe(false);
13 | });
14 |
15 | it('returns false when node is the child of an element having the data-exclude-from-offset-calcs property', () => {
16 | const wrapper = mount(TheAnnotator);
17 | const node = parseHTML('foo
').childNodes[0];
18 | expect(wrapper.vm.contributesToOffsets(node)).toBe(false);
19 | });
20 |
21 | it('returns true when node does not have the data-exclude-from-offset-calcs property', () => {
22 | const wrapper = mount(TheAnnotator);
23 | const node = parseHTML('foo
');
24 | expect(wrapper.vm.contributesToOffsets(node)).toBe(true);
25 | });
26 | });
27 |
28 | });
29 |
--------------------------------------------------------------------------------
/web/frontend/test/libs/example_tocs/colin-miller-best-evidence.txt:
--------------------------------------------------------------------------------
1 | Notices. ii
2 | About the Author iv
3 | About CALI eLangdell Press. v
4 | Table of Contents. vi
5 | Preface. vii
6 | Best Evidence Rule Chapter 1
7 | Introductory Note. 1
8 | I. Historical Origins of the Best Evidence Rule. 1
9 | II. Article X: The Modern Best Evidence Rule. 3
10 | A. Rule 1002: The Rule’s Scope. 3
11 | B. Rule 1001: Defining the Relevant Terms. 8
12 | C. Rule 1003: The Duplicate Exception.. 13
13 | D. Rule 1004: Excusing Nonproduction of Originals. 16
14 | E. Rule 1005: Public Records. 22
15 | F. Rule 1006: Summaries. 24
16 | G. Rule 1007: Admissions. 26
17 | H. Rule 1008: Functions of the Court and Jury. 28
18 | I. The Best Evidence Framework. 30
19 | J. Best Evidence Pleadings. 31
--------------------------------------------------------------------------------
/web/frontend/test/libs/example_tocs/hatfield-ethics-tax-lawyering-3rd.txt:
--------------------------------------------------------------------------------
1 | About the Author iii
2 | Notices. iv
3 | About CALI eLangdell Press. vi
4 | Table of Contents. vii
5 | 1. Introducing Legal Ethics for Tax Lawyers. 1
6 | 1.1. Ethics for Lawyers 1
7 | Notes and Questions. 2
8 | 1.2. The Duty to the Tax System.. 3
9 | Notes and Questions. 3
10 | 1.3. Sharing the Profession with Non-Lawyers. 3
11 | Grace v. Allen.. 4
12 | Notes and Questions. 8
13 | 2. Regulating Tax Lawyering. 11
14 | 2.1. Regulating Tax Lawyering through the IRC.. 11
15 | IRC § 7206. Fraud and false statements. 12
16 | Notes and Questions. 12
17 | § 6694. Understatement of taxpayer's liability by tax return preparer. 14
18 | Notes and Questions. 19
19 | 2.2. Regulating Tax Lawyering through Circular 230. 21
20 | Notes and Questions. 32
21 | WASHBURN v. SHAPIRO... 35
22 | 2.3. Regulating Tax Lawyering through Malpractice Standards. 36
23 | 3. Ethical Problems for Tax Lawyers. 39
24 | 3.1. Tax Opinions and Tax Shelters. 39
25 | Notes and Questions. 43
26 | 3.2. Mistakes. 43
27 | Notes and Questions. 45
28 | 3.3. Working with IRS Lawyers and Other Employees 46
29 | § 10.51 Incompetence and disreputable conduct. 48
30 | Internal Revenue Manual 4.1.1.7.6.1 - Badges of Practitioner Abuse (05-20-2005) 49
31 | From 1991-2 C.B. 1137: 50
32 | Notes and Questions. 51
--------------------------------------------------------------------------------
/web/frontend/test/libs/example_tocs/levin-civil-procedure-pleading.txt:
--------------------------------------------------------------------------------
1 | 1. Preface. 3
2 | 2. Rule 8. General Rules of Pleading. 5
3 | 3. Rule 9. Pleading Special Matters 6
4 | 4. Conley v. Gibson. 6
5 | 5. Swierkiewicz v. Sorema N.A. 9
6 | 6. Rule 10. Form of Pleadings 15
7 | 7. Bell Atlantic Corp. v. Twombly. 15
8 | 8. Ashcroft v. Iqbal 31
9 | 9. Kregler v. City of New York. 45
10 | 10. Complaint 1. 56
11 | 11. Complaint 2. 64
--------------------------------------------------------------------------------
/web/frontend/test/mocha_setup.js:
--------------------------------------------------------------------------------
1 | require("jsdom-global")();
2 |
3 | global.expect = require("expect");
4 | global.DOMParser = window.DOMParser;
5 |
6 | // https://github.com/vuejs/vue-test-utils/issues/936#issuecomment-415386167
7 | window.Date = Date;
8 | global.FRONTEND_URLS = {
9 | search_sources: [],
10 | search_using: [],
11 | new_from_outline: [],
12 | };
13 |
--------------------------------------------------------------------------------
/web/frontend/test/test_helpers.js:
--------------------------------------------------------------------------------
1 | export const parseHTML = (html) => {
2 | const parser = new DOMParser();
3 | const doc = parser.parseFromString(html, "text/html");
4 | return doc.body.children[0];
5 | };
6 |
7 | export const createText = (s) => document.createTextNode(s);
8 |
9 | export const removeVueScopedCSSAttributes = (element) => {
10 | let attrNodes = document.evaluate('//*/attribute::*[starts-with(name(), "data-v-")]', element, null, XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null);
11 |
12 | let attrNode;
13 | while((attrNode = attrNodes.iterateNext())) attrNode.ownerElement.removeAttributeNode(attrNode);
14 | return element;
15 | };
16 |
--------------------------------------------------------------------------------
/web/main/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/main/__init__.py
--------------------------------------------------------------------------------
/web/main/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class MainConfig(AppConfig):
5 | name = "main"
6 |
--------------------------------------------------------------------------------
/web/main/authenticator.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth.backends import ModelBackend
2 |
3 |
4 | class NormalizingAuthenticator(ModelBackend):
5 | def authenticate(self, request, username=None, password=None):
6 | try:
7 | [user, domain] = username.split("@")
8 | email = "@".join([user, domain.lower()])
9 | sup = super().authenticate(request, username=email, password=password)
10 | return sup
11 | except Exception:
12 | return None
13 |
--------------------------------------------------------------------------------
/web/main/hashers.py:
--------------------------------------------------------------------------------
1 | import hashlib
2 |
3 | from django.contrib.auth.hashers import PBKDF2PasswordHasher
4 |
5 |
6 | class PBKDF2WrappedRailsPasswordHasher(PBKDF2PasswordHasher):
7 | """
8 | Legacy password hasher -- see https://docs.djangoproject.com/en/2.2/topics/auth/passwords/#password-upgrading-without-requiring-a-login
9 |
10 | Rails-era passwords are stored as a salted password hashed 20 times with sha512. This legacy hasher wraps those
11 | hashes in our standard PBKDF2 hasher.
12 | """
13 |
14 | algorithm = "pbkdf2_wrapped_rails"
15 |
16 | def encode_rails_hash(self, digest, salt, iterations=None):
17 | """Used by the database migration."""
18 | return super().encode(digest, salt, iterations)
19 |
20 | def encode(self, password, salt, iterations=None):
21 | """Used for checking passwords on login."""
22 | digest = password + salt
23 | for _ in range(20):
24 | digest = hashlib.sha512(digest.encode("utf8")).hexdigest()
25 | return super().encode(digest, salt, iterations)
26 |
--------------------------------------------------------------------------------
/web/main/migrations/0002_auto_20201021_1444.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.16 on 2020-10-21 14:44
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('main', '0001_squashed_0072_auto_20201015_1225'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='case',
15 | name='jurisdiction_id',
16 | field=models.IntegerField(blank=True, null=True),
17 | ),
18 | migrations.AddField(
19 | model_name='case',
20 | name='jurisdiction_slug',
21 | field=models.CharField(blank=True, max_length=20),
22 | ),
23 | migrations.AddField(
24 | model_name='historicalcase',
25 | name='jurisdiction_id',
26 | field=models.IntegerField(blank=True, null=True),
27 | ),
28 | migrations.AddField(
29 | model_name='historicalcase',
30 | name='jurisdiction_slug',
31 | field=models.CharField(blank=True, max_length=20),
32 | ),
33 | ]
34 |
--------------------------------------------------------------------------------
/web/main/migrations/0004_auto_20210405_1927.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.19 on 2021-04-05 19:27
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('main', '0003_alternate_legal_docs'),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name='legaldocumentsource',
15 | name='enabled',
16 | ),
17 | migrations.AddField(
18 | model_name='legaldocumentsource',
19 | name='priority',
20 | field=models.IntegerField(null=True),
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/web/main/migrations/0006_uscodeindex_repealed.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.20 on 2021-04-16 00:42
2 |
3 | from django.db import migrations, models
4 |
5 | def check_repealed(apps, schema_editor):
6 | USCodeIndex = apps.get_model('main', 'USCodeIndex')
7 | for ind in USCodeIndex.objects.all():
8 | ind.repealed = ind.title.startswith("Repealed")
9 | ind.save()
10 |
11 | class Migration(migrations.Migration):
12 |
13 | dependencies = [
14 | ('main', '0005_auto_20210414_1240'),
15 | ]
16 |
17 | operations = [
18 | migrations.AddField(
19 | model_name='uscodeindex',
20 | name='repealed',
21 | field=models.BooleanField(null=True),
22 | ),
23 | migrations.RunPython(check_repealed)
24 | ]
25 |
--------------------------------------------------------------------------------
/web/main/migrations/0007_savedimage.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.21 on 2021-05-05 13:48
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('main', '0006_uscodeindex_repealed'),
11 | ]
12 |
13 | operations = [
14 | migrations.CreateModel(
15 | name='SavedImage',
16 | fields=[
17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18 | ('created_at', models.DateTimeField(auto_now_add=True)),
19 | ('updated_at', models.DateTimeField(auto_now=True)),
20 | ('name', models.CharField(max_length=255)),
21 | ('file_name', models.CharField(max_length=255)),
22 | ('alt_text', models.CharField(max_length=1000)),
23 | ('uploaded_by', models.ForeignKey(on_delete=models.DO_NOTHING, related_name='saved_images', to=settings.AUTH_USER_MODEL)),
24 | ],
25 | options={
26 | 'abstract': False,
27 | },
28 | ),
29 | ]
30 |
--------------------------------------------------------------------------------
/web/main/migrations/0008_auto_20210511_1533.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.21 on 2021-05-11 15:33
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('main', '0007_savedimage'),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name='savedimage',
15 | name='alt_text',
16 | ),
17 | migrations.AlterField(
18 | model_name='savedimage',
19 | name='file_name',
20 | field=models.CharField(blank=True, max_length=255, null=True),
21 | ),
22 | migrations.AlterField(
23 | model_name='savedimage',
24 | name='name',
25 | field=models.CharField(blank=True, max_length=255, null=True),
26 | ),
27 | ]
28 |
--------------------------------------------------------------------------------
/web/main/migrations/0009_auto_20210512_0217.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.21 on 2021-05-12 02:17
2 |
3 | from django.db import migrations, models
4 | import main.storages
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('main', '0008_auto_20210511_1533'),
11 | ]
12 |
13 | operations = [
14 | migrations.RemoveField(
15 | model_name='savedimage',
16 | name='file_name',
17 | ),
18 | migrations.AddField(
19 | model_name='savedimage',
20 | name='image',
21 | field=models.FileField(default=None, storage=main.storages.S3Storage(access_key='accesskey', bucket_name='h2o.images', endpoint_url='http://minio:9000', secret_key='secretkey'), upload_to=''),
22 | preserve_default=False,
23 | ),
24 | ]
25 |
--------------------------------------------------------------------------------
/web/main/migrations/0010_auto_20210512_0257.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.21 on 2021-05-12 02:57
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('main', '0009_auto_20210512_0217'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='savedimage',
15 | name='external_id',
16 | field=models.UUIDField(default=None, unique=True),
17 | preserve_default=False,
18 | ),
19 | migrations.AddIndex(
20 | model_name='savedimage',
21 | index=models.Index(fields=['external_id'], name='main_savedi_externa_97e5f9_idx'),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/web/main/migrations/0011_auto_20210805_1723.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.24 on 2021-08-05 17:23
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('main', '0010_auto_20210512_0257'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='contentnode',
15 | name='headnote_doc_class',
16 | field=models.CharField(blank=True, max_length=40, null=True),
17 | ),
18 | migrations.AddField(
19 | model_name='historicalcontentnode',
20 | name='headnote_doc_class',
21 | field=models.CharField(blank=True, max_length=40, null=True),
22 | ),
23 | migrations.AddField(
24 | model_name='historicaltextblock',
25 | name='doc_class',
26 | field=models.CharField(blank=True, max_length=40, null=True),
27 | ),
28 | migrations.AddField(
29 | model_name='textblock',
30 | name='doc_class',
31 | field=models.CharField(blank=True, max_length=40, null=True),
32 | ),
33 | ]
34 |
--------------------------------------------------------------------------------
/web/main/migrations/0013_casebookfollow.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.24 on 2021-10-04 16:02
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('main', '0012_auto_20210914_0057'),
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='CasebookFollow',
17 | fields=[
18 | ('id', models.BigAutoField(primary_key=True, serialize=False)),
19 | ('created_at', models.DateTimeField(auto_now_add=True)),
20 | ('updated_at', models.DateTimeField(auto_now=True)),
21 | ('casebook', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='main.Casebook')),
22 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
23 | ],
24 | options={
25 | 'unique_together': {('user', 'casebook')},
26 | },
27 | ),
28 | ]
29 |
--------------------------------------------------------------------------------
/web/main/migrations/0014_auto_20211007_1248.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.24 on 2021-10-07 12:48
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('main', '0013_casebookfollow'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='casebook',
15 | name='export_fails',
16 | field=models.IntegerField(default=0),
17 | ),
18 | migrations.AddField(
19 | model_name='historicalcasebook',
20 | name='export_fails',
21 | field=models.IntegerField(default=0),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/web/main/migrations/0015_auto_20211014_0041.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.24 on 2021-10-14 00:41
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('main', '0014_auto_20211007_1248'),
10 | ]
11 |
12 | operations = [
13 | migrations.DeleteModel(
14 | name='Case',
15 | ),
16 | migrations.DeleteModel(
17 | name='HistoricalCase',
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/web/main/migrations/0016_auto_20211025_2147.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.24 on 2021-10-25 21:47
2 |
3 | import django.contrib.postgres.fields
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('main', '0015_auto_20211014_0041'),
11 | ]
12 |
13 | operations = [
14 | migrations.AddField(
15 | model_name='contentnode',
16 | name='display_ordinals',
17 | field=django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), default=list, size=None),
18 | ),
19 | migrations.AddField(
20 | model_name='contentnode',
21 | name='does_display_ordinals',
22 | field=models.BooleanField(default=True),
23 | ),
24 | migrations.AddField(
25 | model_name='historicalcontentnode',
26 | name='display_ordinals',
27 | field=django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), default=list, size=None),
28 | ),
29 | migrations.AddField(
30 | model_name='historicalcontentnode',
31 | name='does_display_ordinals',
32 | field=models.BooleanField(default=True),
33 | ),
34 | ]
35 |
--------------------------------------------------------------------------------
/web/main/migrations/0017_auto_20211130_2114.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.24 on 2021-11-30 19:58
2 |
3 | from django.db import migrations
4 | from django.db.models import F
5 |
6 |
7 | def populate_display_ordinals(apps, schema_editor):
8 | ContentNode = apps.get_model('main', 'ContentNode')
9 | if not ContentNode.objects.exclude(display_ordinals=[]).exists():
10 | ContentNode.objects.all().update(display_ordinals=F('ordinals'))
11 |
12 |
13 | def unset_display_ordinals(apps, schema_editor):
14 | ContentNode = apps.get_model('main', 'ContentNode')
15 | ContentNode.objects.all().update(display_ordinals=[])
16 |
17 |
18 | class Migration(migrations.Migration):
19 |
20 | dependencies = [
21 | ('main', '0016_auto_20211025_2147'),
22 | ]
23 |
24 | operations = [
25 | migrations.RunPython(populate_display_ordinals, unset_display_ordinals),
26 | ]
27 |
--------------------------------------------------------------------------------
/web/main/migrations/0019_livesettings.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.26 on 2022-01-10 20:23
2 |
3 | from django.db import migrations, models
4 |
5 | class Migration(migrations.Migration):
6 |
7 | dependencies = [
8 | ('main', '0018_auto_20211130_2236'),
9 | ]
10 |
11 | operations = [
12 | migrations.CreateModel(
13 | name='LiveSettings',
14 | fields=[
15 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
16 | ('prevent_exports', models.BooleanField(default=False)),
17 | ],
18 | ),
19 | ]
20 |
21 |
--------------------------------------------------------------------------------
/web/main/migrations/0020_auto_20220209_1732.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.27 on 2022-02-09 17:32
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('main', '0019_livesettings'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='livesettings',
15 | name='export_average_rate',
16 | field=models.IntegerField(default=0),
17 | ),
18 | migrations.AddField(
19 | model_name='livesettings',
20 | name='export_last_minute_updated',
21 | field=models.IntegerField(default=0),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/web/main/migrations/0021_auto_20220527_1714.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.28 on 2022-05-27 17:14
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('main', '0020_auto_20220209_1732'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterModelOptions(
14 | name='livesettings',
15 | options={'verbose_name_plural': 'Live settings'},
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/web/main/migrations/0024_drop_raw_headnote.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.14 on 2022-07-06 17:32
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('main', '0023_drop_legacy_annotation_fields'),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name='contentnode',
15 | name='raw_headnote',
16 | ),
17 | migrations.RemoveField(
18 | model_name='historicalcontentnode',
19 | name='raw_headnote',
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/web/main/migrations/0025_drop_created_via_import.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.14 on 2022-07-06 19:33
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('main', '0024_drop_raw_headnote'),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name='historicaltextblock',
15 | name='created_via_import',
16 | ),
17 | migrations.RemoveField(
18 | model_name='textblock',
19 | name='created_via_import',
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/web/main/migrations/0026_drop_public_on_textblock.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.14 on 2022-07-06 20:39
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('main', '0025_drop_created_via_import'),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name='historicaltextblock',
15 | name='public',
16 | ),
17 | migrations.RemoveField(
18 | model_name='textblock',
19 | name='public',
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/web/main/migrations/0027_drop_legacy_tables.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.14 on 2022-07-07 18:08
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('main', '0026_drop_public_on_textblock'),
10 | ]
11 |
12 | operations = [
13 | migrations.DeleteModel(
14 | name='CkeditorAsset',
15 | ),
16 | migrations.DeleteModel(
17 | name='ContentImage',
18 | ),
19 | migrations.DeleteModel(
20 | name='Media',
21 | ),
22 | migrations.DeleteModel(
23 | name='MediaType',
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/web/main/migrations/0028_fulltextsearchindex.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.14 on 2022-07-14 17:36
2 |
3 | import django.contrib.postgres.search
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('main', '0027_drop_legacy_tables'),
11 | ]
12 |
13 | operations = [
14 | migrations.CreateModel(
15 | name='FullTextSearchIndex',
16 | fields=[
17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18 | ('result_id', models.IntegerField()),
19 | ('document', django.contrib.postgres.search.SearchVectorField()),
20 | ('metadata', models.JSONField()),
21 | ('category', models.CharField(max_length=255)),
22 | ],
23 | options={
24 | 'db_table': 'fts_internal_search_view',
25 | 'managed': False,
26 | },
27 | ),
28 | ]
29 |
--------------------------------------------------------------------------------
/web/main/migrations/0029_auto_20220714_1747.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.14 on 2022-07-14 17:47
2 |
3 | from django.db import migrations, models
4 | import main.models
5 | import main.storages
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('main', '0028_fulltextsearchindex'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='casebook',
17 | name='cover_image',
18 | field=models.FileField(blank=True, null=True, storage=main.storages.S3Storage(access_key='accesskey', bucket_name='h2o.images', endpoint_url='http://opencasebook.minio.test:9000', secret_key='secretkey'), upload_to=main.models.cover_image_path),
19 | ),
20 | migrations.AddField(
21 | model_name='historicalcasebook',
22 | name='cover_image',
23 | field=models.TextField(blank=True, max_length=100, null=True),
24 | ),
25 | migrations.AlterField(
26 | model_name='savedimage',
27 | name='image',
28 | field=models.FileField(storage=main.storages.S3Storage(access_key='accesskey', bucket_name='h2o.images', endpoint_url='http://opencasebook.minio.test:9000', secret_key='secretkey'), upload_to=''),
29 | ),
30 | ]
31 |
--------------------------------------------------------------------------------
/web/main/migrations/0030_reading_length.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.14 on 2022-07-13 19:42
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('main', '0029_auto_20220714_1747'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='contentnode',
15 | name='reading_length',
16 | field=models.IntegerField(null=True),
17 | ),
18 | migrations.AddField(
19 | model_name='historicalcontentnode',
20 | name='reading_length',
21 | field=models.IntegerField(null=True),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/web/main/migrations/0031_casebook_description_20220719_1550.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.14 on 2022-07-19 15:50
2 |
3 | import django.core.validators
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('main', '0030_reading_length'),
11 | ]
12 |
13 | operations = [
14 | migrations.AddField(
15 | model_name='casebook',
16 | name='description',
17 | field=models.TextField(blank=True, null=True, validators=[django.core.validators.MaxLengthValidator(750)]),
18 | ),
19 | migrations.AddField(
20 | model_name='historicalcasebook',
21 | name='description',
22 | field=models.TextField(blank=True, null=True, validators=[django.core.validators.MaxLengthValidator(750)]),
23 | ),
24 | ]
25 |
--------------------------------------------------------------------------------
/web/main/migrations/0032_setting_for_html_export.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.14 on 2022-08-01 14:42
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('main', '0031_casebook_description_20220719_1550'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='livesettings',
15 | name='enable_printable_html_export',
16 | field=models.BooleanField(default=False, help_text='Enable the view to export entire casebooks as HTML'),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/web/main/migrations/0035_user_details.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.14 on 2022-08-18 05:37
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('main', '0034_add_instructional_content_field'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='user',
15 | name='personal_site',
16 | field=models.CharField(blank=True, max_length=255),
17 | ),
18 | migrations.AddField(
19 | model_name='user',
20 | name='pronouns',
21 | field=models.CharField(blank=True, max_length=63),
22 | ),
23 | migrations.AddField(
24 | model_name='user',
25 | name='short_bio',
26 | field=models.CharField(blank=True, max_length=511),
27 | ),
28 | ]
29 |
--------------------------------------------------------------------------------
/web/main/migrations/0036_instructional_material_help_text.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.15 on 2022-08-22 14:16
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('main', '0035_user_details'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='contentnode',
15 | name='is_instructional_material',
16 | field=models.BooleanField(default=False, help_text='This content should only be made available on the front end to verified professors'),
17 | ),
18 | migrations.AlterField(
19 | model_name='historicalcontentnode',
20 | name='is_instructional_material',
21 | field=models.BooleanField(default=False, help_text='This content should only be made available on the front end to verified professors'),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/web/main/migrations/0039_drop_printable_livesetting.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.15 on 2022-10-11 15:23
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('main', '0038_initial_institution_data'),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name='livesettings',
15 | name='enable_printable_html_export',
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/web/main/migrations/0040_drop_link_content_type_field.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.16 on 2022-11-14 20:59
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('main', '0039_drop_printable_livesetting'),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name='historicallink',
15 | name='content_type',
16 | ),
17 | migrations.RemoveField(
18 | model_name='link',
19 | name='content_type',
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/web/main/migrations/0043_add_listed_publicly_field.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.18 on 2023-04-18 15:03
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('main', '0042_legaldocument_indexes'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='casebook',
15 | name='listed_publicly',
16 | field=models.BooleanField(db_index=True, default=True, help_text='Whether the casebook, when published, is available in public listings such as H2O search or search engine indexes.'),
17 | ),
18 | migrations.AddField(
19 | model_name='historicalcasebook',
20 | name='listed_publicly',
21 | field=models.BooleanField(db_index=True, default=True, help_text='Whether the casebook, when published, is available in public listings such as H2O search or search engine indexes.'),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/web/main/migrations/0044_add_user_groups.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.18 on 2023-04-18 18:17
2 |
3 | from django.db import migrations
4 | from django.contrib.auth.models import Group
5 |
6 | from main.models import User
7 |
8 |
9 | def create_user_groups(apps, schema_editor):
10 | Group.objects.get_or_create(name='Professor')
11 | Group.objects.get_or_create(name='Student')
12 | Group.objects.get_or_create(name='Librarian')
13 | Group.objects.get_or_create(name='Other')
14 |
15 | def populate_professor_group(apps, schema_editor):
16 | professor = Group.objects.get(name='Professor')
17 |
18 | for prof in User.objects.filter(verified_professor=True):
19 | prof.groups.add(professor)
20 |
21 | class Migration(migrations.Migration):
22 |
23 | dependencies = [
24 | ('main', '0043_add_listed_publicly_field'),
25 | ]
26 |
27 | operations = [
28 | migrations.RunPython(create_user_groups),
29 | migrations.RunPython(populate_professor_group),
30 | ]
31 |
--------------------------------------------------------------------------------
/web/main/migrations/0045_add_courtlistener_api_source.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.18 on 2023-04-12 20:18
2 |
3 | from django.db import migrations
4 | from datetime import date
5 |
6 |
7 | def add_courtlistener(apps, schema_editor):
8 | """Add CourtListener as a first-order type in the UI"""
9 | LegalDocumentSource = apps.get_model("main", "LegalDocumentSource")
10 | if not LegalDocumentSource.objects.filter(name="CourtListener"):
11 | LegalDocumentSource.objects.create(
12 | name="CourtListener",
13 | date_added=date.today(),
14 | last_updated=date.today(),
15 | active=False,
16 | priority=3,
17 | search_class="CourtListener",
18 | short_description="CourtListener searches millions of opinions across hundreds of jurisdictions.",
19 | )
20 |
21 |
22 | class Migration(migrations.Migration):
23 |
24 | dependencies = [
25 | ("main", "0044_add_user_groups"),
26 | ]
27 |
28 | operations = [
29 | migrations.RunPython(add_courtlistener),
30 | ]
31 |
--------------------------------------------------------------------------------
/web/main/migrations/0046_auto_20240516_1322.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.25 on 2024-05-16 13:22
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('main', '0045_add_courtlistener_api_source'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='contentnode',
15 | name='ali_licensed',
16 | field=models.BooleanField(default=False),
17 | ),
18 | migrations.AddField(
19 | model_name='historicalcontentnode',
20 | name='ali_licensed',
21 | field=models.BooleanField(default=False),
22 | )
23 | ]
--------------------------------------------------------------------------------
/web/main/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/main/migrations/__init__.py
--------------------------------------------------------------------------------
/web/main/templates/400.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block page_title %}400 Bad Request{% endblock %}
4 |
5 | {% block mainContent %}
6 |
7 |
8 |
Bad Request
9 |
The browser (or proxy) sent a request that this server could not understand. If you require assistance, please contact us .
10 |
11 |
12 | {% endblock %}
13 |
--------------------------------------------------------------------------------
/web/main/templates/403.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block page_title %}403 Forbidden{% endblock %}
4 |
5 | {% block mainContent %}
6 |
7 |
8 |
Permission Denied
9 |
You don't appear to have access to the requested resource. If you believe you reached this page in error, please contact us .
10 |
11 |
12 | {% endblock %}
13 |
--------------------------------------------------------------------------------
/web/main/templates/403_csrf.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block page_title %}403 Login Request Failed{% endblock %}
4 |
5 | {% block mainContent %}
6 |
7 |
8 |
Login Request Failed
9 |
Your attempt to log in failed. If you believe you reached this page in error, please contact us .
10 |
11 |
12 | {% endblock %}
13 |
--------------------------------------------------------------------------------
/web/main/templates/404.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block page_title %}404 Page Not Found{% endblock %}
4 |
5 | {% block mainContent %}
6 |
7 |
8 |
Oops!
9 |
We can't seem to find the page you are looking for.
10 |
To report a broken link, please contact us .
11 |
12 |
13 | {% endblock %}
14 |
--------------------------------------------------------------------------------
/web/main/templates/500.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block page_title %}500 Internal Server Error{% endblock %}
4 |
5 | {% block mainContent %}
6 |
7 |
8 |
Oops!
9 |
There’s been an internal server error preparing this page, but don't worry, we'll fix it.
10 |
Please contact us and let us know how you found yourself here. Thanks!
11 |
12 |
13 | {% endblock %}
14 |
--------------------------------------------------------------------------------
/web/main/templates/admin/change_form_with_richeditor.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/change_form.html" %}
2 | {% load render_bundle from webpack_loader %}
3 |
4 | {% block footer %}
5 | {{ block.super }}
6 | {% render_bundle 'chunk-common' %}
7 | {% render_bundle 'rich_text_editor' %}
8 | {% endblock %}
9 |
--------------------------------------------------------------------------------
/web/main/templates/admin/h2o_index.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/index.html" %}
2 |
3 | {% block userlinks %}
4 |
5 | Usage /
6 | {{ block.super }}
7 |
8 | {% endblock userlinks %}
--------------------------------------------------------------------------------
/web/main/templates/admin/input_filter.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 | {% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}
4 |
5 |
6 | {% with choices.0 as all_choice %}
7 |
18 | {% endwith %}
19 |
20 |
21 |
--------------------------------------------------------------------------------
/web/main/templates/admin/main/casebook/change_form.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/change_form_with_richeditor.html" %}
2 |
--------------------------------------------------------------------------------
/web/main/templates/admin/main/legaldocument/change_form.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/change_form_with_richeditor.html" %}
2 | {% load admin_urls %}
3 |
4 | {% block after_field_sets %}
5 | {{ block.super }}
6 |
11 |
16 | {% endblock %}
17 |
18 | {% block submit_buttons_bottom %}
19 | {{ block.super }}
20 | {% endblock %}
21 |
--------------------------------------------------------------------------------
/web/main/templates/admin/main/link/change_form.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/change_form.html" %}
2 | {% load admin_urls %}
3 |
4 |
--------------------------------------------------------------------------------
/web/main/templates/admin/main/resource/change_form.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/change_form_with_richeditor.html" %}
2 | {% load admin_urls %}
3 |
4 | {% block after_related_objects %}
5 | {{ block.super }}
6 |
7 |
Resource
8 |
14 |
20 |
27 |
28 | {% endblock %}
29 |
--------------------------------------------------------------------------------
/web/main/templates/admin/main/section/change_form.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/change_form_with_richeditor.html" %}
2 |
--------------------------------------------------------------------------------
/web/main/templates/admin/main/textblock/change_form.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/change_form_with_richeditor.html" %}
2 | {% load admin_urls %}
3 |
4 | {% block after_field_sets %}
5 | {{ block.super }}
6 |
11 | {% endblock %}
12 |
13 |
14 |
--------------------------------------------------------------------------------
/web/main/templates/archived_casebooks.html:
--------------------------------------------------------------------------------
1 |
2 | {% extends 'base.html' %}
3 |
4 | {% block custom_skip_target %}{% endblock %}
5 |
6 | {% block mainContent %}
7 | {# This appears to be solely for spacing #}
8 |
9 | Main Content
10 |
11 |
My Archived Casebooks
12 |
13 |
14 |
Archived Casebooks do not appear in search results and are not viewable by other users.
15 | {% include "includes/content_browser.html" with content=user.archived_casebooks %}
16 |
17 |
18 | {% endblock %}
19 |
--------------------------------------------------------------------------------
/web/main/templates/casebook_outline_edit.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% if edit_mode or clone_section_targets %}
4 | {% load render_bundle from webpack_loader %}
5 | {% load crispy_forms_tags %}
6 | {% endif %}
7 |
8 | {% block page_title %} {% if mode %}{{mode}} | {% endif %} {{casebook.title}} {% if section %}: {{ section.title }} {% endif %} {% endblock %}
9 |
10 | {% if editing %}
11 | {% block extra_foot %}{% render_bundle 'rich_text_editor' %}{% endblock %}
12 | {% endif %}
13 |
14 | {% block banner %}
15 | {% include 'includes/preview_banner.html' %}
16 | {% endblock %}
17 |
18 | {% block mainContent %}
19 | {% include 'includes/casebook_page_tabs.html' %}
20 |
21 |
29 | {% endblock %}
30 |
--------------------------------------------------------------------------------
/web/main/templates/casebook_page_credits.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block page_title %} {% if mode %}{{mode}} | {% endif %} {{casebook.title}} {% if section %}: {{ section.title }} {% endif %} {% endblock %}
4 |
5 |
6 | {% block banner %}
7 | {% include 'includes/preview_banner.html' %}
8 | {% endblock %}
9 |
10 | {% block mainContent %}
11 | {% include 'includes/casebook_page_tabs.html' %}
12 |
13 |
14 |
15 |
16 | {% include 'includes/credits.html' %}
17 |
18 |
19 |
20 | {% endblock %}
21 |
--------------------------------------------------------------------------------
/web/main/templates/casebook_page_related.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block page_title %} {% if mode %}{{mode}} | {% endif %} {{casebook.title}} {% if section %}: {{ section.title }} {% endif %} {% endblock %}
4 |
5 |
6 | {% block banner %}
7 | {% include 'includes/preview_banner.html' %}
8 | {% endblock %}
9 |
10 | {% block mainContent %}
11 | {% include 'includes/casebook_page_tabs.html' %}
12 |
13 |
21 | {% endblock %}
22 |
--------------------------------------------------------------------------------
/web/main/templates/casebook_page_search.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block page_title %} {% if mode %}{{mode}} | {% endif %} {{casebook.title}} {% if section %}: {{ section.title }} {% endif %} {% endblock %}
4 |
5 |
6 | {% block banner %}
7 | {% include 'includes/preview_banner.html' %}
8 | {% endblock %}
9 |
10 | {% block mainContent %}
11 | {% include 'includes/casebook_page_tabs.html' %}
12 |
13 |
14 |
15 |
16 | {% include 'includes/casebook_search.html' %}
17 |
18 | {% include "includes/casebook_copyright_notice.html" %}
19 |
20 |
21 |
22 | {% endblock %}
23 |
--------------------------------------------------------------------------------
/web/main/templates/export/as_printable_html/credits.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Acknowledgments
4 |
5 |
6 | Some materials included in this export came from the following casebooks.
7 |
8 |
9 | {% for source in cloned_from %}
10 |
11 | {{source.title}}
12 | by
13 |
14 | {% for author in source.primary_authors %}
15 | {% if not forloop.first %}, {% endif %}
16 | {{ author.display_name }}
17 | {% endfor %}
18 |
19 |
20 | {% endfor %}
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/web/main/templates/export/as_printable_html/tbd.html:
--------------------------------------------------------------------------------
1 | {% if node.type == 'section' %}
2 | {{ node.ordinal_string }}
3 | {{ node.title }}
4 | {% if node.subtitle %}
5 | {{ node.subtitle }}
6 | {% endif %}
7 | {% if node.headnote %}
8 | {{ node.headnote_for_export }}
9 | {% endif %}
10 | TBD
11 |
--------------------------------------------------------------------------------
/web/main/templates/export/credits.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Acknowledgments
5 |
6 |
7 | Some materials included in this export came from the following casebooks.
8 |
9 |
10 | {% for source in cloned_from %}
11 |
12 | {{source.title}}
13 | by
14 |
15 | {% for author in source.primary_authors %}
16 | {% if not forloop.first %}, {% endif %}
17 | {{ author.display_name }}
18 | {% endfor %}
19 |
20 |
21 | {% endfor %}
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/web/main/templates/export/section.html:
--------------------------------------------------------------------------------
1 |
2 | {% include "export/node.html" with index=0 node=node %}
3 | {% with is_child=True %}
4 | {% for child in children %}
5 | {% include "export/node.html" with index=forloop.counter node=child %}
6 | {% endfor %}
7 | {% endwith %}
8 |
9 |
--------------------------------------------------------------------------------
/web/main/templates/export/tbd.html:
--------------------------------------------------------------------------------
1 | {% if node.type == 'section' %}
2 | {{ node.ordinal_string }}
3 | {{ node.title }}
4 | {% if node.subtitle %}
5 | {{ node.subtitle }}
6 | {% endif %}
7 | {% if node.headnote %}
8 | {{ node.headnote_for_export }}
9 | {% endif %}
10 | TBD
11 |
--------------------------------------------------------------------------------
/web/main/templates/export_error.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block page_title %}Export error{% endblock %}
4 |
5 | {% block mainContent %}
6 |
7 |
8 |
Export error
9 |
We're having trouble exporting this casebook currently. Our team has been notified and we're looking into the issue.
10 |
For now, you can return to the casebook
11 |
12 |
13 | {% endblock %}
14 |
--------------------------------------------------------------------------------
/web/main/templates/includes/analytics.html:
--------------------------------------------------------------------------------
1 | {% if USE_ANALYTICS %}
2 | {# Ensure that MATOMO_SITE_ID and MATOMO_SITE_URL are both set for this environment #}
3 |
18 | {% endif %}
19 |
--------------------------------------------------------------------------------
/web/main/templates/includes/announcement_banner.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/main/templates/includes/bodies/case.html:
--------------------------------------------------------------------------------
1 |
2 | {% include 'includes/case_header.html' with case=section.resource %}
3 |
4 |
5 |
--------------------------------------------------------------------------------
/web/main/templates/includes/bodies/empty.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/main/templates/includes/bodies/empty.html
--------------------------------------------------------------------------------
/web/main/templates/includes/bodies/legal_doc.html:
--------------------------------------------------------------------------------
1 |
2 | {% include section.resource.header_template with legal_doc=section.resource %}
3 |
4 |
5 |
--------------------------------------------------------------------------------
/web/main/templates/includes/bodies/link.html:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/web/main/templates/includes/bodies/text_block.html:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/web/main/templates/includes/breadcrumbs.html:
--------------------------------------------------------------------------------
1 | {% load call_method %}
2 | {% load humanize_minutes %}
3 | {% if content.ordinals %}
4 |
5 | {{ content.type | title }}
6 | {% call_method content 'ordinals_with_urls' editing=editing as ordinals %}
7 | {% for ordinal in ordinals %}
8 | {% if forloop.last %}
9 |
10 | {% if content.does_display_ordinals %}
11 | {{ ordinal.ordinal }}
12 | {% endif %}
13 |
14 | {% else %}
15 |
{{ ordinal.ordinal }}
16 |
.
17 | {% endif %}
18 | {% endfor %}
19 | {% if content.reading_time %}
20 |
21 | {{ content.reading_time | humanize_minutes }}
22 | {% if content.num_links %}
23 | + {{ content.num_links }} link{{ content.num_links|pluralize }}
24 | {% endif %}
25 |
26 | {% endif %}
27 |
28 | {% endif %}
29 |
--------------------------------------------------------------------------------
/web/main/templates/includes/case_header.html:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/web/main/templates/includes/casebook_copyright_notice.html:
--------------------------------------------------------------------------------
1 |
2 | This book, and all H2O books, are Creative Commons licensed
3 | for sharing and re-use with the exception of certain excerpts.
4 |
5 |
6 |
7 | {% if section.ali_licensed %}
8 | {{ section.get_ali_license_text }}
9 | {% else %}
10 | Any excerpts from the Restatements of the Law , Principles of the Law , and the Model Penal Code are copyright by The American Law Institute. Excerpts are reproduced with permission, not as part of a Creative Commons license.
11 | {% endif %}
12 |
13 |
14 |
--------------------------------------------------------------------------------
/web/main/templates/includes/casebook_page_tabs.html:
--------------------------------------------------------------------------------
1 |
7 | {% if tabs %}
8 |
9 |
10 |
11 | {% for name, link, is_active_tab in tabs %}
12 | {% if is_active_tab %}
13 |
{{name}}
14 | {% else %}
15 |
{{name}}
16 | {% endif %}
17 | {% endfor %}
18 |
19 |
20 |
21 | {% endif %}
22 |
23 |
--------------------------------------------------------------------------------
/web/main/templates/includes/collaborators.html:
--------------------------------------------------------------------------------
1 | {% for user in content.primary_authors %}
2 |
5 | {% endfor %}
6 |
--------------------------------------------------------------------------------
/web/main/templates/includes/featured_casebook.html:
--------------------------------------------------------------------------------
1 | {% if casebook %}
2 |
3 |
4 | {{ title }}
9 | {% if authors %}
10 | by
11 |
12 | {% for author in authors %}
13 |
14 | {% if authors|length > 1 and forloop.last %}
15 | and
16 | {% endif %}
17 | {{ author.display_name }}
18 |
19 | {% endfor %}
20 |
21 | {% endif %}
22 |
23 | {% else %}
24 |
25 | {% endif %}
26 |
27 |
--------------------------------------------------------------------------------
/web/main/templates/includes/footer.html:
--------------------------------------------------------------------------------
1 |
2 |
H2O
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
H2O was built at Harvard Law School by the Library Innovation Lab.
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/web/main/templates/includes/headnote.html:
--------------------------------------------------------------------------------
1 | {% if content.headnote or content.subtitle %}
2 |
3 |
4 | {{ content.headnote | safe }}
5 |
6 |
7 | {% else %}
8 |
9 | {% endif %}
10 |
--------------------------------------------------------------------------------
/web/main/templates/includes/legal_doc_sources/cap_header.html:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/web/main/templates/includes/legal_doc_sources/court_listener_header.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/main/templates/includes/legal_doc_sources/empty_header.html:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/web/main/templates/includes/legal_doc_sources/gpo_header.html:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/web/main/templates/includes/page_buttons.html:
--------------------------------------------------------------------------------
1 | {% with prev_node_url=previous_and_next_urls.0 next_node_url=previous_and_next_urls.1 %}
2 |
3 |
4 | {% if prev_node_url %}
5 |
10 | {% endif %}
11 |
12 | {% if next_node_url %}
13 |
18 | {% endif %}
19 |
20 |
21 | {% endwith %}
22 |
--------------------------------------------------------------------------------
/web/main/templates/includes/reading_mode_toc_item.html:
--------------------------------------------------------------------------------
1 | {% load reading_mode_toc_item %}
2 |
3 |
4 |
5 | {% for child in toc %}
6 |
7 |
8 | {{ child.ordinal_string|default:"—" }}
9 |
10 |
11 | {{ child.title }}
12 |
13 | {% if child.children and child.id == top_level_node.id %}
14 | {% reading_mode_toc_item child.children casebook top_level_node %}
15 | {% endif %}
16 |
17 |
18 | {% endfor %}
19 |
--------------------------------------------------------------------------------
/web/main/templates/includes/sentry.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/main/templates/includes/table-of-contents.html:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/web/main/templates/legal_doc.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 |
3 | {% block page_title %}{{ legal_doc.get_name }} | Legal Documents{% endblock %}
4 |
5 | {% block mainContent %}
6 |
7 |
8 |
9 |
10 | {% include legal_doc.header_template %}
11 |
14 |
15 |
16 |
17 | {% endblock %}
18 |
--------------------------------------------------------------------------------
/web/main/templates/registration/login.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load crispy_forms_tags %}
3 | {% block page_title %}Log in{% endblock %}
4 |
5 | {% block mainContent %}
6 |
13 |
14 |
15 |
22 |
23 |
Need help?
24 |
29 |
30 |
31 |
32 | {% endblock %}
33 |
--------------------------------------------------------------------------------
/web/main/templates/registration/password_change_done.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load crispy_forms_tags %}
3 | {% block page_title %}Password changed{% endblock %}
4 |
5 | {% block mainContent %}
6 |
7 |
14 |
15 |
16 |
Your password has been updated.
17 |
18 |
19 |
20 | {% endblock %}
21 |
--------------------------------------------------------------------------------
/web/main/templates/registration/password_change_form.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load crispy_forms_tags %}
3 | {% block page_title %}Change password{% endblock %}
4 |
5 | {% block mainContent %}
6 |
13 |
23 | {% endblock %}
24 |
--------------------------------------------------------------------------------
/web/main/templates/registration/password_reset_complete.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load crispy_forms_tags %}
3 | {% block page_title %}Password changed{% endblock %}
4 |
5 | {% block mainContent %}
6 |
7 |
14 |
15 |
16 |
Your password has been updated. You can now log in .
17 |
18 |
19 |
20 | {% endblock %}
21 |
--------------------------------------------------------------------------------
/web/main/templates/registration/password_reset_confirm.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load crispy_forms_tags %}
3 | {% block page_title %}Password reset confirmation{% endblock %}
4 |
5 | {% block mainContent %}
6 | {% if not validlink %}{% endif %}
7 |
14 |
15 |
16 | {% if validlink %}
17 |
Please enter your new password twice so we can verify you typed it in correctly.
18 |
19 |
23 |
24 | {% else %}
25 |
The password reset link was invalid, possibly because it has already been used. Please request a new password reset.
26 | {% endif %}
27 |
28 |
29 | {% if not validlink %}
{% endif %}
30 | {% endblock %}
31 |
--------------------------------------------------------------------------------
/web/main/templates/registration/password_reset_done.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load crispy_forms_tags %}
3 | {% block page_title %}Password changed{% endblock %}
4 |
5 | {% block mainContent %}
6 |
7 |
14 |
15 |
16 |
We've emailed you instructions for setting your password, if an account exists with the email you entered. You should receive them shortly.
17 |
If you don't receive an email, please make sure you've entered the address you registered with, and check your spam folder.
18 |
19 |
20 |
21 | {% endblock %}
22 |
--------------------------------------------------------------------------------
/web/main/templates/registration/password_reset_form.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load crispy_forms_tags %}
3 | {% block page_title %}Password reset{% endblock %}
4 |
5 | {% block mainContent %}
6 |
13 |
14 |
15 |
Forgotten your password? Enter your email address below, and we'll email instructions for setting a new one.
16 |
17 |
21 |
22 |
23 |
24 | {% endblock %}
25 |
--------------------------------------------------------------------------------
/web/main/templates/registration/sign_up.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load crispy_forms_tags %}
3 | {% block page_title %}Sign up{% endblock %}
4 |
5 | {% block mainContent %}
6 |
13 |
14 |
15 |
16 | {% crispy form %}
17 |
18 |
19 |
20 | {% endblock %}
21 |
--------------------------------------------------------------------------------
/web/main/templates/robots.txt:
--------------------------------------------------------------------------------
1 | User-Agent: *
2 | Disallow: /*/export
3 |
4 | {% for casebook in excluded_casebooks %}
5 | Disallow: {{ casebook.get_absolute_url }}*
6 | {% endfor %}
--------------------------------------------------------------------------------
/web/main/templates/user_edit.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load crispy_forms_tags %}
3 | {% block page_title %}Your Profile{% endblock %}
4 |
5 | {% block mainContent %}
6 |
7 |
8 |
9 |
10 | {% crispy form %}
11 |
12 |
13 |
14 | {% endblock %}
15 |
--------------------------------------------------------------------------------
/web/main/templatetags/call_method.py:
--------------------------------------------------------------------------------
1 | from django import template
2 |
3 | register = template.Library()
4 |
5 |
6 | @register.simple_tag
7 | def call_method(obj, method, *args, **kwargs):
8 | """
9 | Call a method on an object and return the result. Example:
10 |
11 | {% call_method casebook has_collaborator request.user as has_collaborator %}
12 |
13 | Useful for migrating Rails templates that have assignment statements at the top.
14 | """
15 | return getattr(obj, method)(*args, **kwargs)
16 |
--------------------------------------------------------------------------------
/web/main/templatetags/current_query_string.py:
--------------------------------------------------------------------------------
1 | import urllib.parse
2 | from django import template
3 |
4 | register = template.Library()
5 |
6 |
7 | @register.simple_tag(takes_context=True)
8 | def current_query_string(context, **kwargs):
9 | """
10 | Given {% current_query_string page=1 q='' %}, return the current query string but with page and q values changed.
11 | """
12 | return urllib.parse.urlencode(dict(context["request"].GET, **kwargs), doseq=True)
13 |
--------------------------------------------------------------------------------
/web/main/templatetags/export_node_html.py:
--------------------------------------------------------------------------------
1 | from django import template
2 |
3 | from main.export import annotated_content_for_export
4 | from main.models import ContentNode
5 |
6 | register = template.Library()
7 |
8 |
9 | @register.simple_tag
10 | def export_node_html(node: ContentNode, export_options: dict = None, *args, **kwargs):
11 | return annotated_content_for_export(node, export_options)
12 |
--------------------------------------------------------------------------------
/web/main/templatetags/featured_casebook.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 | from django import template
3 | from main.models import Casebook
4 |
5 | register = template.Library()
6 |
7 |
8 | @register.inclusion_tag("includes/featured_casebook.html")
9 | def featured_casebook(
10 | id: int,
11 | title: Optional[str] = None,
12 | authors: Optional[str] = None,
13 | cover_image: Optional[str] = None,
14 | ):
15 | """
16 | Render a casebook on the Featured Casebooks page, optionally overriding metadata on the casebook object itself
17 | """
18 | casebook = Casebook.objects.filter(id=id).first()
19 |
20 | if not casebook:
21 | return {"error": f"Casebook ID {id} was not found in this environment"}
22 |
23 | if not casebook.is_public:
24 | return {"error": f"Casebook ID {id} is not publicly viewable"}
25 |
26 | return {
27 | "casebook": casebook,
28 | "title": title or casebook.title,
29 | "authors": (
30 | [{"display_name": authors}]
31 | if authors
32 | else [author for author in casebook.primary_authors if author.verified_professor]
33 | ),
34 | "cover_image": cover_image,
35 | "error": None,
36 | }
37 |
--------------------------------------------------------------------------------
/web/main/templatetags/humanize_minutes.py:
--------------------------------------------------------------------------------
1 | """
2 | Django's 'humanize' tags don't include methods for humanizing
3 | durations of time (only timestamps).
4 | These methods do that.
5 | """
6 |
7 | from django import template
8 |
9 | register = template.Library()
10 |
11 |
12 | @register.filter
13 | def humanize_minutes(minutes):
14 | # eesh this is messy with a lot of special cases
15 | if minutes < 1:
16 | return "less than a minute"
17 | if minutes < 2:
18 | return "1 minute"
19 | if minutes < 45:
20 | return f"{minutes:0.0f} minutes"
21 | halves = (minutes * 2) // 60
22 | if halves == 1:
23 | return "half an hour"
24 | if halves == 2:
25 | return "1 hour"
26 | if halves % 2:
27 | return f"{halves / 2:0.1f} hours"
28 | return f"{halves / 2:0.0f} hours"
29 |
--------------------------------------------------------------------------------
/web/main/templatetags/reading_mode_toc_item.py:
--------------------------------------------------------------------------------
1 | from django import template
2 | from main.models import Casebook, ContentNode
3 |
4 | register = template.Library()
5 |
6 |
7 | @register.inclusion_tag("includes/reading_mode_toc_item.html")
8 | def reading_mode_toc_item(toc: dict, casebook: Casebook, top_level_node: ContentNode):
9 | """
10 | Render one level of node in the reading mode TOC
11 | """
12 |
13 | return {"toc": toc, "casebook": casebook, "top_level_node": top_level_node}
14 |
--------------------------------------------------------------------------------
/web/main/templatetags/short_page_range.py:
--------------------------------------------------------------------------------
1 | from django import template
2 |
3 | register = template.Library()
4 |
5 |
6 | @register.filter
7 | def short_page_range(page, padding=2):
8 | """
9 | Return just the page numbers we want to display from a Django Page object returned by a Paginator.
10 | E.g., assuming we are on page 10 of 20:
11 | {% for num in page|short_page_range %}num, {% endfor %}
12 | Will output:
13 | 1, 2, ..., 8, 9, 10, 11, 12, ..., 19, 20
14 | """
15 | paginator = page.paginator
16 | show_ellipsis = True
17 | for i in paginator.page_range:
18 | if i <= 2 or abs(i - page.number) <= padding or i >= paginator.num_pages - 1:
19 | yield i
20 | show_ellipsis = True
21 | elif show_ellipsis:
22 | show_ellipsis = False
23 | yield "..."
24 |
--------------------------------------------------------------------------------
/web/main/templatetags/string_strip.py:
--------------------------------------------------------------------------------
1 | from django import template
2 |
3 | register = template.Library()
4 |
5 |
6 | @register.filter
7 | def string_strip(content: str, string_from: str) -> str:
8 | "Remove a substring."
9 | return content.replace(string_from, "")
10 |
--------------------------------------------------------------------------------
/web/main/test/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/main/test/__init__.py
--------------------------------------------------------------------------------
/web/main/test/functional/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/main/test/functional/__init__.py
--------------------------------------------------------------------------------
/web/main/test/functional/fixtures/contentcollaborators.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "model": "main.contentcollaborator",
4 | "pk": 1,
5 | "fields": {
6 | "created_at": "2022-10-13T20:15:29.544",
7 | "updated_at": "2022-10-13T20:15:29.544",
8 | "has_attribution": true,
9 | "can_edit": true,
10 | "user": 1,
11 | "casebook": 1
12 | }
13 | },
14 | {
15 | "model": "main.contentcollaborator",
16 | "pk": 2,
17 | "fields": {
18 | "created_at": "2022-10-13T20:15:29.544",
19 | "updated_at": "2022-10-13T20:15:29.544",
20 | "has_attribution": true,
21 | "can_edit": true,
22 | "user": 2,
23 | "casebook": 2
24 | }
25 | },
26 | {
27 | "model": "main.contentcollaborator",
28 | "pk": 3,
29 | "fields": {
30 | "created_at": "2022-10-13T20:15:29.544",
31 | "updated_at": "2022-10-13T20:15:29.544",
32 | "has_attribution": true,
33 | "can_edit": true,
34 | "user": 3,
35 | "casebook": 1
36 | }
37 | },
38 | {
39 | "model": "main.contentcollaborator",
40 | "pk": 4,
41 | "fields": {
42 | "created_at": "2023-02-21T15:35:50.241",
43 | "updated_at": "2023-02-21T15:35:50.241",
44 | "has_attribution": true,
45 | "can_edit": true,
46 | "user": 1,
47 | "casebook": 3
48 | }
49 | }
50 | ]
51 |
--------------------------------------------------------------------------------
/web/main/test/test_publishing.py:
--------------------------------------------------------------------------------
1 | from main.models import Casebook
2 |
3 | from django.urls import reverse
4 |
5 |
6 | def test_publish_new_casebook(private_casebook, client):
7 | """Newly-composed (private, never-published) casebooks, when published, become public"""
8 | assert private_casebook.state == Casebook.LifeCycle.PRIVATELY_EDITING.value
9 |
10 | response = client.post(
11 | reverse("publish", args=[private_casebook]),
12 | as_user=private_casebook.testing_editor,
13 | )
14 | assert response.status_code == 200
15 | private_casebook.refresh_from_db()
16 | assert private_casebook.is_public
17 |
18 |
19 | def test_publish_draft(casebook, client):
20 | """Drafts of already-published casebooks, when published, replace their parent."""
21 |
22 | assert casebook.is_public
23 | draft = casebook.make_draft()
24 | draft.title = "new title"
25 | draft.save()
26 | assert casebook.title != draft.title
27 |
28 | response = client.post(
29 | reverse("publish", args=[draft]),
30 | as_user=casebook.testing_editor,
31 | )
32 | assert response.status_code == 200
33 | casebook.refresh_from_db()
34 | assert casebook.is_public
35 | assert casebook.title == draft.title
36 |
--------------------------------------------------------------------------------
/web/main/test/test_templatetags.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from main.templatetags.featured_casebook import featured_casebook
4 |
5 |
6 | def test_featured_casebook(full_casebook):
7 | """Featuring a casebook should return information about it, unless overridden"""
8 | assert featured_casebook(full_casebook.id)["title"] == full_casebook.title
9 | assert featured_casebook(full_casebook.id, title="Fake title")["title"] == "Fake title"
10 | assert (
11 | featured_casebook(full_casebook.id, authors="Fake authors")["authors"][0]["display_name"]
12 | == "Fake authors"
13 | )
14 | assert featured_casebook(full_casebook.id)["error"] is None
15 |
16 |
17 | def test_featured_casebook_private(private_casebook):
18 | """A private casebook should return only an error message"""
19 | assert "not publicly viewable" in featured_casebook(private_casebook.id)["error"]
20 |
21 |
22 | @pytest.mark.django_db
23 | def test_featured_casebook_missing():
24 | """Attempting to retrieve a non-existent casebook should return a friendly error message"""
25 | assert "not found" in featured_casebook(-1)["error"]
26 |
--------------------------------------------------------------------------------
/web/main/test/views.py:
--------------------------------------------------------------------------------
1 | from django.core.exceptions import PermissionDenied, SuspiciousOperation
2 | from django.http import Http404
3 | from django.views.decorators.csrf import requires_csrf_token
4 |
5 |
6 | def raise_400(request):
7 | raise SuspiciousOperation("Fishy")
8 |
9 |
10 | def raise_403(request):
11 | raise PermissionDenied
12 |
13 |
14 | @requires_csrf_token
15 | def raise_403_csrf(request):
16 | pass # pragma: no cover
17 |
18 |
19 | def raise_404(request):
20 | raise Http404("Does not exist")
21 |
22 |
23 | def raise_500(request):
24 | raise Exception("Oops")
25 |
--------------------------------------------------------------------------------
/web/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Django's command-line utility for administrative tasks."""
3 | import os
4 | import sys
5 |
6 |
7 | def main():
8 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
9 | try:
10 | from django.core.management import execute_from_command_line
11 | except ImportError as exc:
12 | raise ImportError(
13 | "Couldn't import Django. Are you sure it's installed and "
14 | "available on your PYTHONPATH environment variable? Did you "
15 | "forget to activate a virtual environment?"
16 | ) from exc
17 | execute_from_command_line(sys.argv)
18 |
19 |
20 | if __name__ == "__main__":
21 | main()
22 |
--------------------------------------------------------------------------------
/web/reporting/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/reporting/__init__.py
--------------------------------------------------------------------------------
/web/reporting/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class ReportingConfig(AppConfig):
5 | default_auto_field = "django.db.models.BigAutoField"
6 | name = "reporting"
7 |
--------------------------------------------------------------------------------
/web/reporting/migrations/0002_plural_name_reporting.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.14 on 2022-08-08 14:40
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('reporting', '0001_add_reporting_proxy_models'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterModelOptions(
14 | name='casebookseries',
15 | options={'ordering': ('created_at',), 'verbose_name_plural': 'Casebooks in series'},
16 | ),
17 | migrations.AlterModelOptions(
18 | name='casebookseriesprof',
19 | options={'ordering': ('created_at',), 'verbose_name_plural': 'Casebooks in series by professors'},
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/web/reporting/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/reporting/migrations/__init__.py
--------------------------------------------------------------------------------
/web/reporting/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from . import views
4 |
5 | urlpatterns = [
6 | path("stats/", views.matomo_stats, name="matomo-stats"),
7 | path(
8 | "time-series/professor-casebooks",
9 | views.professor_casebook_timeseries,
10 | name="reporting-professor-casebook-timeseries",
11 | ),
12 | path(
13 | "time-series/casebooks",
14 | views.casebook_timeseries,
15 | name="reporting-casebook-timeseries",
16 | ),
17 | path(
18 | "time-series/professors-published",
19 | views.professor_cumulative_publication_timeseries,
20 | name="reporting-professors-published-timeseries",
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/web/static/as_printable_html/print.js:
--------------------------------------------------------------------------------
1 | import { Previewer } from "./pagedjs.js";
2 |
3 | console.log("Starting pagination");
4 |
5 | const paged = new Previewer();
6 |
7 | paged
8 | .preview(
9 | document.querySelector("main"),
10 | [window.css],
11 | document.querySelector("#pdf-output")
12 | )
13 | .then((flow) => {
14 | console.log("Rendered", flow.total, "pages.");
15 | document.querySelector("main").remove();
16 | document.querySelector("#pdf-output").style.visibility = "visible";
17 | });
18 |
--------------------------------------------------------------------------------
/web/static/as_printable_html/uscode.css:
--------------------------------------------------------------------------------
1 | h4.subsection-head {
2 | font-weight: bold;
3 | font-size: 22px;
4 | }
5 |
6 | h4.notes-section {
7 | margin-top: 6rem;
8 | font-weight: bold;
9 | font-size: 22px;
10 | }
11 |
12 | h4.paragraph-head {
13 | padding-left: 1rem;
14 | }
15 |
16 | header.uscode-header {
17 | text-align: center;
18 | font-size: 18px;
19 | font-weight: bold;
20 | }
21 | header.uscode-header .citation {
22 | font-size: 18px;
23 | }
24 |
--------------------------------------------------------------------------------
/web/static/dist/fonts/AtlasGrotesk-Light.bcfd7cf6.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/dist/fonts/AtlasGrotesk-Light.bcfd7cf6.woff
--------------------------------------------------------------------------------
/web/static/dist/fonts/AtlasGrotesk-Light.be1c731f.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/dist/fonts/AtlasGrotesk-Light.be1c731f.eot
--------------------------------------------------------------------------------
/web/static/dist/fonts/AtlasGrotesk-Light.e69872f9.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/dist/fonts/AtlasGrotesk-Light.e69872f9.ttf
--------------------------------------------------------------------------------
/web/static/dist/fonts/AtlasGrotesk-Medium.14791ebe.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/dist/fonts/AtlasGrotesk-Medium.14791ebe.eot
--------------------------------------------------------------------------------
/web/static/dist/fonts/AtlasGrotesk-Medium.83bc0a62.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/dist/fonts/AtlasGrotesk-Medium.83bc0a62.woff
--------------------------------------------------------------------------------
/web/static/dist/fonts/AtlasGrotesk-Medium.d9cc5003.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/dist/fonts/AtlasGrotesk-Medium.d9cc5003.ttf
--------------------------------------------------------------------------------
/web/static/dist/fonts/AtlasGrotesk-Regular.1f499573.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/dist/fonts/AtlasGrotesk-Regular.1f499573.ttf
--------------------------------------------------------------------------------
/web/static/dist/fonts/AtlasGrotesk-Regular.94001d71.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/dist/fonts/AtlasGrotesk-Regular.94001d71.eot
--------------------------------------------------------------------------------
/web/static/dist/fonts/AtlasGrotesk-Regular.d2997fb4.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/dist/fonts/AtlasGrotesk-Regular.d2997fb4.woff
--------------------------------------------------------------------------------
/web/static/dist/fonts/ChronicleTextG3-Roman.6a453768.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/dist/fonts/ChronicleTextG3-Roman.6a453768.ttf
--------------------------------------------------------------------------------
/web/static/dist/fonts/ChronicleTextG3-Roman.f77c0515.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/dist/fonts/ChronicleTextG3-Roman.f77c0515.woff
--------------------------------------------------------------------------------
/web/static/dist/fonts/ChronicleTextG3-Roman.f9c467a3.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/dist/fonts/ChronicleTextG3-Roman.f9c467a3.eot
--------------------------------------------------------------------------------
/web/static/dist/fonts/RobotoMono-Bold.c0c4a337.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/dist/fonts/RobotoMono-Bold.c0c4a337.ttf
--------------------------------------------------------------------------------
/web/static/dist/fonts/glyphicons-halflings-regular.448c34a5.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/dist/fonts/glyphicons-halflings-regular.448c34a5.woff2
--------------------------------------------------------------------------------
/web/static/dist/fonts/glyphicons-halflings-regular.e18bbf61.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/dist/fonts/glyphicons-halflings-regular.e18bbf61.ttf
--------------------------------------------------------------------------------
/web/static/dist/fonts/glyphicons-halflings-regular.f4769f9b.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/dist/fonts/glyphicons-halflings-regular.f4769f9b.eot
--------------------------------------------------------------------------------
/web/static/dist/fonts/glyphicons-halflings-regular.fa277232.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/dist/fonts/glyphicons-halflings-regular.fa277232.woff
--------------------------------------------------------------------------------
/web/static/dist/img/AtlasGrotesk-Light.219d1463.svg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/dist/img/AtlasGrotesk-Light.219d1463.svg
--------------------------------------------------------------------------------
/web/static/dist/img/AtlasGrotesk-Medium.3e829f26.svg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/dist/img/AtlasGrotesk-Medium.3e829f26.svg
--------------------------------------------------------------------------------
/web/static/dist/img/AtlasGrotesk-Regular.26133821.svg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/dist/img/AtlasGrotesk-Regular.26133821.svg
--------------------------------------------------------------------------------
/web/static/dist/img/ChronicleTextG3-Roman.0e3646cf.svg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/dist/img/ChronicleTextG3-Roman.0e3646cf.svg
--------------------------------------------------------------------------------
/web/static/dist/img/Link.148d855f.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/web/static/dist/img/add-casebook.ff004159.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/web/static/dist/img/add-material.d109215f.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | add-material
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/web/static/dist/img/add-section.a7b07d92.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | add-section
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/web/static/dist/img/cancel-icon.101f571f.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | button/cancel
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/web/static/dist/img/edit-icon.d38c8fff.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | edit
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/web/static/dist/img/expand-arrow.6bf1cea3.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/web/static/dist/img/export-html.a6acb1d4.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/web/static/dist/img/external-link-icon.3e1fd22b.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
--------------------------------------------------------------------------------
/web/static/dist/img/follow.2e7d7ca7.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
9 |
10 |
16 |
17 |
--------------------------------------------------------------------------------
/web/static/dist/img/landing-demo.769625d8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/dist/img/landing-demo.769625d8.png
--------------------------------------------------------------------------------
/web/static/dist/img/link-go.4122dfcb.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/web/static/dist/img/lock.0ee492f3.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/static/dist/img/logo-icon-white-on-blue.f2cf816b.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/static/dist/img/next_page.13726d8a.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/web/static/dist/img/prev_page.8adf0763.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/web/static/dist/img/search-icon.8c383980.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/web/static/dist/img/take-notes-icon.26352bcb.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/web/static/dist/js/main.8272c3ff.js:
--------------------------------------------------------------------------------
1 | (function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/static/dist/",n(n.s=2)})({2:function(e,t,n){e.exports=n("cd89")},cd89:function(e,t,n){}});
2 | //# sourceMappingURL=main.8272c3ff.js.map
--------------------------------------------------------------------------------
/web/static/fonts/AtlasGrotesk-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/fonts/AtlasGrotesk-Bold.woff2
--------------------------------------------------------------------------------
/web/static/fonts/AtlasGrotesk-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/fonts/AtlasGrotesk-Regular.woff2
--------------------------------------------------------------------------------
/web/static/fonts/ChronicleTextG3-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/fonts/ChronicleTextG3-Bold.woff2
--------------------------------------------------------------------------------
/web/static/fonts/ChronicleTextG3-Italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/fonts/ChronicleTextG3-Italic.woff2
--------------------------------------------------------------------------------
/web/static/fonts/ChronicleTextG3-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/fonts/ChronicleTextG3-Regular.woff2
--------------------------------------------------------------------------------
/web/static/fonts/LibreCaslonText-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/fonts/LibreCaslonText-Bold.woff2
--------------------------------------------------------------------------------
/web/static/fonts/LibreCaslonText-Italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/fonts/LibreCaslonText-Italic.woff2
--------------------------------------------------------------------------------
/web/static/fonts/LibreCaslonText-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/fonts/LibreCaslonText-Regular.woff2
--------------------------------------------------------------------------------
/web/static/images/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/.keep
--------------------------------------------------------------------------------
/web/static/images/Link.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/web/static/images/add-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/add-icon.png
--------------------------------------------------------------------------------
/web/static/images/arrow-search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/arrow-search.png
--------------------------------------------------------------------------------
/web/static/images/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/close.png
--------------------------------------------------------------------------------
/web/static/images/endorsers/cohen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/endorsers/cohen.png
--------------------------------------------------------------------------------
/web/static/images/endorsers/fried.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/endorsers/fried.png
--------------------------------------------------------------------------------
/web/static/images/endorsers/fried2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/endorsers/fried2.png
--------------------------------------------------------------------------------
/web/static/images/endorsers/modirzadeh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/endorsers/modirzadeh.png
--------------------------------------------------------------------------------
/web/static/images/endorsers/quinn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/endorsers/quinn.png
--------------------------------------------------------------------------------
/web/static/images/endorsers/suk-gersen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/endorsers/suk-gersen.png
--------------------------------------------------------------------------------
/web/static/images/endorsers/zittrain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/endorsers/zittrain.png
--------------------------------------------------------------------------------
/web/static/images/expand-arrow.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/web/static/images/external-link-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
--------------------------------------------------------------------------------
/web/static/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/favicon.ico
--------------------------------------------------------------------------------
/web/static/images/h20-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/h20-logo.png
--------------------------------------------------------------------------------
/web/static/images/icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/icons.png
--------------------------------------------------------------------------------
/web/static/images/landing-demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/landing-demo.png
--------------------------------------------------------------------------------
/web/static/images/logo-icon-blue-on-yellow.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/static/images/logo-icon-white-on-blue.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/static/images/logo-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/logo-white.png
--------------------------------------------------------------------------------
/web/static/images/mockups/landing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/mockups/landing.png
--------------------------------------------------------------------------------
/web/static/images/mockups/new-casebook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/mockups/new-casebook.png
--------------------------------------------------------------------------------
/web/static/images/quickbar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/quickbar.png
--------------------------------------------------------------------------------
/web/static/images/repeat_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/repeat_bg.png
--------------------------------------------------------------------------------
/web/static/images/school-logos/berkeley.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/school-logos/berkeley.png
--------------------------------------------------------------------------------
/web/static/images/school-logos/harvard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/school-logos/harvard.png
--------------------------------------------------------------------------------
/web/static/images/school-logos/michigan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/school-logos/michigan.png
--------------------------------------------------------------------------------
/web/static/images/school-logos/penn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/school-logos/penn.png
--------------------------------------------------------------------------------
/web/static/images/school-logos/stanford.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/school-logos/stanford.png
--------------------------------------------------------------------------------
/web/static/images/school-logos/yale.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/school-logos/yale.png
--------------------------------------------------------------------------------
/web/static/images/search-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/web/static/images/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/search.png
--------------------------------------------------------------------------------
/web/static/images/take-notes-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/web/static/images/tinymce_icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/tinymce_icons.png
--------------------------------------------------------------------------------
/web/static/images/transparent.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/transparent.gif
--------------------------------------------------------------------------------
/web/static/images/ui/casebook/add-casebook.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/web/static/images/ui/casebook/add-material.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | add-material
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/web/static/images/ui/casebook/add-section.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | add-section
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/web/static/images/ui/casebook/cancel-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | button/cancel
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/web/static/images/ui/casebook/edit-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | edit
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/web/static/images/ui/casebook/export-html.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/web/static/images/ui/casebook/follow.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
9 |
10 |
16 |
17 |
--------------------------------------------------------------------------------
/web/static/images/ui/casebook/link-go.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/web/static/images/ui/casebook/lock.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/static/images/ui/casebook/next_page.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/web/static/images/ui/casebook/prev_page.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/web/static/images/ui/edit/add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/ui/edit/add.png
--------------------------------------------------------------------------------
/web/static/images/ui/edit/elide.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/ui/edit/elide.png
--------------------------------------------------------------------------------
/web/static/images/ui/verified-large.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/ui/verified-large.png
--------------------------------------------------------------------------------
/web/static/images/ui/verified.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/images/ui/verified.png
--------------------------------------------------------------------------------
/web/static/tinymce_skin/README.txt:
--------------------------------------------------------------------------------
1 | This directory was manually copied from tinymce-5.1.4 by this command:
2 |
3 | cp -r node_modules/tinymce/skins/ui/oxide static/tinymce_skin
4 |
5 | The docs suggest that this manual copy could be avoided using webpack file-loader, but this did not immediately work
6 | when I tried it:
7 |
8 | https://www.tiny.cloud/docs/advanced/usage-with-module-loaders/#webpackfile-loader
--------------------------------------------------------------------------------
/web/static/tinymce_skin/content.mobile.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Tiny Technologies, Inc. All rights reserved.
3 | * Licensed under the LGPL or a commercial license.
4 | * For LGPL see License.txt in the project root for license information.
5 | * For commercial licenses see https://www.tiny.cloud/
6 | */
7 | .tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection {
8 | /* Note: this file is used inside the content, so isn't part of theming */
9 | background-color: green;
10 | display: inline-block;
11 | opacity: 0.5;
12 | position: absolute;
13 | }
14 | body {
15 | -webkit-text-size-adjust: none;
16 | }
17 | body img {
18 | /* this is related to the content margin */
19 | max-width: 96vw;
20 | }
21 | body table img {
22 | max-width: 95%;
23 | }
24 | body {
25 | font-family: sans-serif;
26 | }
27 | table {
28 | border-collapse: collapse;
29 | }
30 |
--------------------------------------------------------------------------------
/web/static/tinymce_skin/content.mobile.min.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Tiny Technologies, Inc. All rights reserved.
3 | * Licensed under the LGPL or a commercial license.
4 | * For LGPL see License.txt in the project root for license information.
5 | * For commercial licenses see https://www.tiny.cloud/
6 | */
7 | .tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{background-color:green;display:inline-block;opacity:.5;position:absolute}body{-webkit-text-size-adjust:none}body img{max-width:96vw}body table img{max-width:95%}body{font-family:sans-serif}table{border-collapse:collapse}
8 | /*# sourceMappingURL=content.mobile.min.css.map */
9 |
--------------------------------------------------------------------------------
/web/static/tinymce_skin/fonts/tinymce-mobile.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/static/tinymce_skin/fonts/tinymce-mobile.woff
--------------------------------------------------------------------------------
/web/test/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/test/__init__.py
--------------------------------------------------------------------------------
/web/test/files/export/export-casebook-no-annotations.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/test/files/export/export-casebook-no-annotations.docx
--------------------------------------------------------------------------------
/web/test/files/export/export-casebook-with-annotations.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/test/files/export/export-casebook-with-annotations.docx
--------------------------------------------------------------------------------
/web/test/files/export/export-resource-no-annotations.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/test/files/export/export-resource-no-annotations.docx
--------------------------------------------------------------------------------
/web/test/files/export/export-resource-with-annotations.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/test/files/export/export-resource-with-annotations.docx
--------------------------------------------------------------------------------
/web/test/files/export/export-section-no-annotations.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/test/files/export/export-section-no-annotations.docx
--------------------------------------------------------------------------------
/web/test/files/export/export-section-with-annotations.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harvard-lil/h2o/c67c4761179b03c1245fcbf7a560ac698a8f8704/web/test/files/export/export-section-with-annotations.docx
--------------------------------------------------------------------------------