Teardown old project list frontend code (#11950)

* Remove frontend project list angular code (pugfiles + controllers)

* Remove unused translation keys in every language

GitOrigin-RevId: e0514721d6a32ca2de7d1be9433da3476ba1680c
This commit is contained in:
M Fahru 2023-05-25 09:48:46 -07:00 committed by Copybot
parent c7a80f9228
commit 3e315eada1
33 changed files with 1 additions and 2370 deletions

View file

@ -1,80 +0,0 @@
extends ../layout
block vars
- var suppressNavContentLinks = true
block append meta
meta(name="ol-projects" data-type="json" content=projects)
meta(name="ol-tags" data-type="json" content=tags)
meta(name="ol-notifications" data-type="json" content=notifications)
meta(name="ol-notificationsInstitution" data-type="json" content=notificationsInstitution)
meta(name="ol-userAffiliations" data-type="json" content=userAffiliations)
meta(name="ol-userEmails" data-type="json" content=userEmails)
meta(name="ol-userHasNoSubscription" data-type="boolean" content=!!(settings.enableSubscriptions && !hasSubscription))
meta(name="ol-allInReconfirmNotificationPeriods" data-type="json" content=allInReconfirmNotificationPeriods)
meta(name="ol-reconfirmedViaSAML" content=reconfirmedViaSAML)
meta(name="ol-survey-name" data-type="string" content=(survey ? survey.name : undefined))
meta(name="ol-groupsAndEnterpriseBannerVariant" data-type="string" content=groupsAndEnterpriseBannerVariant)
block content
main.content.content-alt.project-list-page#main-content(
ng-controller="ProjectPageController"
role="main"
)
.system-messages(
ng-cloak
ng-controller="SystemMessagesController"
)
.system-message(
ng-repeat="message in messages"
ng-controller="SystemMessageController"
ng-hide="hidden"
)
button(ng-hide="protected",ng-click="hide()").close.pull-right
span(aria-hidden="true") ×
span.sr-only #{translate("close")}
.system-message-content
| {{htmlContent}}
include ../translations/translation_message
.project-list-content(event-tracking=settings.overleaf ? "loads_v2_dash" : "", onboard=settings.overleaf ? "true" : "", event-tracking-trigger=settings.overleaf ? "load" : "", event-tracking-mb="true", event-segmentation="{location: 'dash', v2_onboard: true}")
.project-list-row(ng-cloak)
.project-list-container.row(ng-if="projects.length > 0")
.project-list-sidebar-wrapper.col-md-2.col-xs-3
aside.project-list-sidebar
include ./list/side-bar
if (survey && survey.name)
.project-list-sidebar-survey(
ng-if="shouldShowSurveyLink"
ng-cloak
)
| #{survey.preText}
a.project-list-sidebar-survey-link(
href=survey.url
target="_blank"
rel="noreferrer noopener"
) #{survey.linkText}
button.project-list-sidebar-survey-dismiss(
type="button"
title="Dismiss Overleaf survey"
ng-click="dismissSurvey()"
)
span(
aria-hidden="true"
) ×
.project-list-main.col-md-10.col-xs-9
include ./list/notifications
include ./list/project-list
.project-list-empty.row(ng-if="projects.length === 0")
.project-list-empty-col.col-md-offset-2.col-md-8.col-md-offset-2.col-xs-8.col-xs-offset-2
include ./list/empty-project-list
.row.row-spaced
.col-sm-12
include ./list/notifications
include ./list/modals

View file

@ -1,112 +0,0 @@
- var featuresPageVariant = splitTestVariants && splitTestVariants['features-page'] ? splitTestVariants['features-page'] : 'default'
- var featuresLink = featuresPageVariant === 'new' ? "/about/features-overview" : "/learn/how-to/Overleaf_premium_features"
mixin current_plan()
if (usersBestSubscription)
.text-right.pull-right.current-plan
case usersBestSubscription.type
when 'free'
+free_plan()
when 'individual'
if (usersBestSubscription.remainingTrialDays >= 0)
+individual_plan_trial(usersBestSubscription.subscription, usersBestSubscription.plan, usersBestSubscription.remainingTrialDays)
else
+individual_plan_active(usersBestSubscription.subscription, usersBestSubscription.plan)
when 'group'
if (usersBestSubscription.remainingTrialDays >= 0)
+group_plan_trial(usersBestSubscription.subscription, usersBestSubscription.plan, usersBestSubscription.remainingTrialDays)
else
+group_plan_active(usersBestSubscription.subscription, usersBestSubscription.plan)
when 'commons'
+commons_plan(usersBestSubscription.subscription, usersBestSubscription.plan)
mixin individual_plan_trial(subscription, plan, remainingTrialDays)
a.current-plan-label(
tooltip=translate('plan_tooltip', { plan: plan.name }),
tooltip-placement="bottom"
href=featuresLink
event-tracking="features-page-link"
event-tracking-mb="true"
event-tracking-trigger="click"
event-segmentation={splitTest:"features-page", splitTestVariant: featuresPageVariant}
)
if (remainingTrialDays === 1)
| !{translate('trial_last_day')}
span.info-badge
else
| !{translate('trial_remaining_days', { days: remainingTrialDays })}
span.info-badge
mixin individual_plan_active(subscription, plan)
a.current-plan-label(
tooltip=translate('plan_tooltip', {plan: plan.name}),
tooltip-placement="bottom"
href=featuresLink
event-tracking="features-page-link"
event-tracking-mb="true"
event-tracking-trigger="click"
event-segmentation={splitTest:"features-page", splitTestVariant: featuresPageVariant}
)
| !{translate('premium_plan_label')}
span.info-badge
mixin group_plan_trial(subscription, plan, remainingTrialDays)
a.current-plan-label(
tooltip=translate(subscription.teamName != null ? 'group_plan_with_name_tooltip' : 'group_plan_tooltip', { plan: plan.name, groupName: subscription.teamName }),
tooltip-placement="bottom"
href=featuresLink
event-tracking="features-page-link"
event-tracking-mb="true"
event-tracking-trigger="click"
event-segmentation={splitTest:"features-page", splitTestVariant: featuresPageVariant}
)
if (remainingTrialDays === 1)
| !{translate('trial_last_day')}
span.info-badge
else
| !{translate('trial_remaining_days', { days: remainingTrialDays })}
span.info-badge
mixin group_plan_active(subscription, plan)
a.current-plan-label(
tooltip=translate(subscription.teamName != null ? 'group_plan_with_name_tooltip' : 'group_plan_tooltip', { plan: plan.name, groupName: subscription.teamName }),
tooltip-placement="bottom"
href=featuresLink
event-tracking="features-page-link"
event-tracking-mb="true"
event-tracking-trigger="click"
event-segmentation={splitTest:"features-page", splitTestVariant: featuresPageVariant}
)
| !{translate('premium_plan_label')}
span.info-badge
mixin commons_plan(subscription, plan)
a.current-plan-label(
tooltip=translate('commons_plan_tooltip', { plan: plan.name, institution: subscription.name }),
tooltip-placement="bottom"
href=featuresLink
event-tracking="features-page-link"
event-tracking-mb="true"
event-tracking-trigger="click"
event-segmentation={splitTest:"features-page", splitTestVariant: featuresPageVariant}
)
| !{translate('premium_plan_label')}
span.info-badge
mixin free_plan()
a.current-plan-label(
tooltip=translate('free_plan_tooltip'),
tooltip-placement="bottom"
href=featuresLink
event-tracking="features-page-link"
event-tracking-mb="true"
event-tracking-trigger="click"
event-segmentation={splitTest:"features-page", splitTestVariant: featuresPageVariant}
)
| !{translate('free_plan_label')}
span.info-badge
|
a.btn.btn-primary(
href="/user/subscription/plans?itm_referrer=project-dashboard-upgrade-prompt"
ng-click="sendUpgradeButtonClickEvent()"
) Upgrade

View file

@ -1,82 +0,0 @@
.row.row-spaced
.col-xs-12
.card.card-thin
div.welcome.text-centered(ng-cloak)
h2 #{translate("welcome_to_sl")}
p #{translate("new_to_latex_look_at")}
a(
href="/templates"
event-tracking="welcome-page-templates-click"
event-tracking-mb="true"
event-tracking-trigger="click"
event-segmentation='{"project-dashboard-react": "default"}'
) #{translate("templates").toLowerCase()}
| #{translate("or")}
a(
href="/learn"
event-tracking="welcome-page-latex-help-click"
event-tracking-mb="true"
event-tracking-trigger="click"
event-segmentation='{"project-dashboard-react": "default"}'
) #{translate("latex_help_guide")}
.row
.col-md-offset-4.col-md-4
.dropdown.minimal-create-proj-dropdown(dropdown)
a.btn.btn-primary.dropdown-toggle(
href="#",
data-toggle="dropdown",
dropdown-toggle
event-tracking="welcome-page-create-first-project-click"
event-tracking-mb="true"
event-tracking-trigger="click"
event-segmentation='{"project-dashboard-react": "default", "dropdownMenu": "main-button", "dropdownOpen": "null" }'
)
//- We can't know if dropdown is open or not, so will send "dropdownOpen: null" as a segmentation above
| Create First Project
ul.dropdown-menu.minimal-create-proj-dropdown-menu(role="menu")
li
a(
href,
ng-click="openCreateProjectModal()"
event-tracking="welcome-page-create-first-project-click"
event-tracking-mb="true"
event-tracking-trigger="click"
event-segmentation='{"project-dashboard-react": "default", "dropdownMenu": "blank-project", "dropdownOpen": "true" }'
) #{translate("blank_project")}
li
a(
href,
ng-click="openCreateProjectModal('example')"
event-tracking="welcome-page-create-first-project-click"
event-tracking-mb="true"
event-tracking-trigger="click"
event-segmentation='{"project-dashboard-react": "default", "dropdownMenu": "example-project", "dropdownOpen": "true" }'
) #{translate("example_project")}
li
a(
href,
ng-click="openUploadProjectModal()"
event-tracking="welcome-page-create-first-project-click"
event-tracking-mb="true"
event-tracking-trigger="click"
event-segmentation='{"project-dashboard-react": "default", "dropdownMenu": "upload-project", "dropdownOpen": "true" }'
) #{translate("upload_project")}
!= moduleIncludes("newProjectMenu", locals)
if (templates)
li.divider
li.dropdown-header #{translate("templates")}
each item in templates
- var eventSegmentation = '{"project-dashboard-react": "default", "dropdownMenu":"' + item.trackingKey + '", "dropdownOpen": "true" }'
li
a.menu-indent(
href=item.url
event-tracking="welcome-page-create-first-project-click"
event-tracking-mb="true"
event-tracking-trigger="click"
event-segmentation=eventSegmentation
) #{translate(item.name)}

View file

@ -1,136 +0,0 @@
td.project-list-table-name-cell
.project-list-table-name-container
input.project-list-table-select-item(
select-individual,
type="checkbox",
ng-model="project.selected"
stop-propagation="click"
aria-label=translate('select_project', {project: '{{ project.name }}'})
)
span.project-list-table-name
a.project-list-table-name-link(
ng-href="{{projectLink(project)}}"
stop-propagation="click"
) {{project.name}}
span(
ng-controller="TagListController"
)
.tag-label(
ng-repeat='tag in project.tags'
stop-propagation="click"
)
button.label.label-default.tag-label-name(
ng-click="selectTag(tag)"
aria-label="Select tag {{ tag.name }}"
)
i.fa.fa-circle(
aria-hidden="true"
ng-style="{ 'color': 'hsl({{ getHueForTagId(tag._id) }}, 70%, 45%)' }"
)
| {{tag.name}}
button.label.label-default.tag-label-remove(
ng-click="removeProjectFromTag(project, tag)"
aria-label="Remove tag {{ tag.name }}"
)
span(aria-hidden="true") ×
td.project-list-table-owner-cell
span.owner(ng-if='project.owner') {{getOwnerName(project)}}
|  
i.fa.fa-question-circle.small(
ng-if="hasGenericOwnerName()"
tooltip="This project is owned by a user who hasnt yet migrated their account to Overleaf v2"
tooltip-append-to-body="true"
aria-hidden="true"
)
span(ng-if="isLinkSharingProject(project)")
|  
i.fa.fa-link.small(
tooltip=translate("link_sharing")
tooltip-placement="right"
tooltip-append-to-body="true"
aria-label=translate("link_sharing")
)
td.project-list-table-lastupdated-cell
span.last-modified(tooltip="{{project.lastUpdated | formatDate}}")
| {{project.lastUpdated | fromNowDate}}
span(ng-show='project.lastUpdatedBy')
|
| #{translate('by')}
| {{getUserName(project.lastUpdatedBy)}}
td.project-list-table-actions-cell
div
button.btn.btn-link.action-btn(
ng-if="!(project.archived || project.trashed)"
aria-label=translate('copy'),
tooltip=translate('copy'),
tooltip-placement="top",
tooltip-append-to-body="true",
ng-click="openCloneProjectModal(project)"
)
i.icon.fa.fa-files-o(aria-hidden="true")
button.btn.btn-link.action-btn(
aria-label=translate('download'),
tooltip=translate('download'),
tooltip-placement="top",
tooltip-append-to-body="true",
ng-click="download($event)"
)
i.icon.fa.fa-cloud-download(aria-hidden="true")
button.btn.btn-link.action-btn(
ng-if="!project.archived"
aria-label=translate('archive'),
tooltip=translate('archive'),
tooltip-placement="top",
tooltip-append-to-body="true",
ng-click="archive($event)"
)
i.icon.fa.fa-inbox(aria-hidden="true")
button.btn.btn-link.action-btn(
ng-if="!project.trashed"
aria-label=translate('trash'),
tooltip=translate('trash'),
tooltip-placement="top",
tooltip-append-to-body="true",
ng-click="trash($event)"
)
i.icon.fa.fa-trash(aria-hidden="true")
button.btn.btn-link.action-btn(
ng-if="project.archived && !project.trashed"
aria-label=translate('unarchive'),
tooltip=translate('unarchive'),
tooltip-placement="top",
tooltip-append-to-body="true",
ng-click="unarchive($event)"
)
i.icon.fa.fa-reply(aria-hidden="true")
button.btn.btn-link.action-btn(
ng-if="project.trashed && !project.archived"
aria-label=translate('untrash'),
tooltip=translate('untrash'),
tooltip-placement="top",
tooltip-append-to-body="true",
ng-click="untrash($event)"
)
i.icon.fa.fa-reply(aria-hidden="true")
button.btn.btn-link.action-btn(
ng-if="project.trashed && !project.archived && !isOwner()"
aria-label=translate('leave'),
tooltip=translate('leave'),
tooltip-placement="top",
tooltip-append-to-body="true",
ng-click="leave($event)"
)
i.icon.fa.fa-sign-out(aria-hidden="true")
button.btn.btn-link.action-btn(
ng-if="project.trashed && !project.archived && isOwner()"
aria-label=translate('delete'),
tooltip=translate('delete'),
tooltip-placement="top",
tooltip-append-to-body="true",
ng-click="delete($event)"
)
i.icon.fa.fa-ban(aria-hidden="true")

View file

@ -1,286 +0,0 @@
include ../../_mixins/reconfirm_affiliation
.user-notifications(ng-controller="NotificationsController")
ul.list-unstyled(
ng-if="notifications.length > 0 && projects.length > 0",
ng-cloak
)
li.notification-entry(
ng-repeat="notification in notifications"
)
div(ng-switch="notification.templateKey" ng-hide="notification.hide")
.alert.alert-info(
ng-switch-when="notification_project_invite",
ng-controller="ProjectInviteNotificationController"
)
.notification-body
span(ng-show="!notification.accepted") !{translate("notification_project_invite_message", { userName: "{{ userName }}", projectName: "{{ projectName }}" })}
span(ng-show="notification.accepted") !{translate("notification_project_invite_accepted_message", { projectName: "{{ projectName }}" })}
.notification-action
a.pull-right.btn.btn-sm.btn-info(
ng-show="notification.accepted",
href="/project/{{ notification.messageOpts.projectId }}"
) #{translate("open_project")}
a.pull-right.btn.btn-sm.btn-info(
href,
ng-click="accept()",
ng-disabled="notification.inflight",
ng-show="!notification.accepted"
)
span(ng-show="!notification.inflight") #{translate("join_project")}
span(ng-show="notification.inflight")
i.fa.fa-fw.fa-spinner.fa-spin(aria-hidden="true")
|  
| #{translate("joining")}…
.notification-close
button(ng-click="dismiss(notification)").close.pull-right
span(aria-hidden="true") ×
span.sr-only #{translate("close")}
.alert.alert-info(
ng-switch-when="wfh_2020_upgrade_offer"
)
.notification-body
span Important notice: Your free WFH2020 upgrade came to an end on June 30th 2020. We're still providing a number of special initiatives to help you continue collaborating throughout 2020.
.notification-action
a.pull-right.btn.btn-sm.btn-info(href="https://www.overleaf.com/events/wfh2020") View
.notification-close
button(ng-click="dismiss(notification)").close.pull-right
span(aria-hidden="true") ×
span.sr-only #{translate("close")}
.alert.alert-info(
ng-switch-when="notification_ip_matched_affiliation"
ng-if="notification.messageOpts.ssoEnabled"
)
.notification-body
| !{translate("looks_like_youre_at", {institutionName: '{{notification.messageOpts.university_name}}'}, ['strong'])}
br
| !{translate("you_can_now_log_in_sso", {}, ['strong'])}
br
| #{translate("link_institutional_email_get_started", {}, ['strong'])} 
a(
ng-href="{{notification.messageOpts.portalPath || 'https://www.overleaf.com/learn/how-to/Institutional_Login'}}"
) #{translate("find_out_more_nt")}
.notification-action
a.pull-right.btn.btn-sm.btn-info(
ng-href=`{{samlInitPath}}?university_id={{notification.messageOpts.institutionId}}&auto=/project`
)
| #{translate("link_account")}
.notification-close
button.btn-sm(ng-click="dismiss(notification)").close.pull-right
span(aria-hidden="true") ×
span.sr-only #{translate("close")}
.alert.alert-info(
ng-switch-when="notification_ip_matched_affiliation"
ng-if="!notification.messageOpts.ssoEnabled"
)
.notification-body
| !{translate("looks_like_youre_at", {institutionName: '{{notification.messageOpts.university_name}}'}, ['strong'])}
br
| !{translate("did_you_know_institution_providing_professional", {institutionName: '{{notification.messageOpts.university_name}}'}, ['strong'])}
br
| #{translate("add_email_to_claim_features")}
.notification-action
a.pull-right.btn.btn-sm.btn-info(
href="/user/settings"
) #{translate("add_affiliation")}
.notification-close
button.btn-sm(ng-click="dismiss(notification)").close.pull-right
span(aria-hidden="true") ×
span.sr-only #{translate("close")}
.alert.alert-danger(
ng-switch-when="notification_tpds_file_limit"
)
.notification-body
| Error: Your project
strong {{ notification.messageOpts.projectName }}
| has gone over the 2000 file limit using an integration (e.g. Dropbox or GitHub) <br/>
| Please decrease the size of your project to prevent further errors.
.notification-action
a.pull-right.btn.btn-sm.btn-info(href="/user/settings")
| Account Settings
.notification-close
button.btn-sm(ng-click="dismiss(notification)").close.pull-right
span(aria-hidden="true") &times;
span.sr-only #{translate("close")}
.alert.alert-warning(
ng-switch-when="notification_dropbox_duplicate_project_names"
)
.notification-body
p()
| !{translate("dropbox_duplicate_project_names", { projectName: '{{notification.messageOpts.projectName}}'}, ['strong'])}
p()
| !{translate("dropbox_duplicate_project_names_suggestion", {}, ['strong'])}
| &nbsp;
a(href="/learn/how-to/Dropbox_Synchronization#Troubleshooting") #{translate("learn_more")}
|.
.notification-close
button.btn-sm(ng-click="dismiss(notification)").close.pull-right
span(aria-hidden="true") &times;
span.sr-only #{translate("close")}
.alert.alert-info(
ng-switch-when="notification_dropbox_unlinked_due_to_lapsed_reconfirmation"
)
.notification-body
if user.features.dropbox
| !{translate("dropbox_unlinked_premium_feature", {}, ['strong'])}
| !{translate("can_now_relink_dropbox", {}, [{name: 'a', attrs: {href: '/user/settings#project-sync' }}])}
else
| !{translate("dropbox_unlinked_premium_feature", {}, ['strong'])}
| !{translate("confirm_affiliation_to_relink_dropbox")}
| &nbsp;
a(href="/learn/how-to/Institutional_Email_Reconfirmation") #{translate("learn_more")}
.notification-close
button.btn-sm(ng-click="dismiss(notification)").close.pull-right
span(aria-hidden="true") &times;
span.sr-only #{translate("close")}
.alert.alert-info(
ng-switch-default
)
span(ng-bind-html="notification.html").notification-body
.notification-close
button(ng-click="dismiss(notification)").close.pull-right
span(aria-hidden="true") &times;
span.sr-only #{translate("close")}
ul.list-unstyled(
ng-if="notificationsInstitution.length > 0",
ng-cloak
)
li.notification-entry(
ng-repeat="notification in notificationsInstitution"
)
div(ng-switch="notification.templateKey" ng-hide="notification.hide")
.alert.alert-info(
ng-switch-when="notification_institution_sso_available"
)
.notification-body
p !{translate("can_link_institution_email_acct_to_institution_acct", {appName: settings.appName, email: "{{notification.email}}", institutionName: "{{notification.institutionName}}"})}
div !{translate("doing_this_allow_log_in_through_institution", {appName: settings.appName})}&nbsp;
a(href="/learn/how-to/Institutional_Login") #{translate("learn_more")}
.notification-action
a.btn.btn-sm.btn-info(ng-href="{{samlInitPath}}?university_id={{notification.institutionId}}&auto=/project&email={{notification.email}}")
| #{translate('link_account')}
.alert.alert-info(
ng-switch-when="notification_institution_sso_linked"
)
.notification-body
div !{translate("account_has_been_link_to_institution_account", {appName: settings.appName, email: "{{notification.email}}", institutionName: "{{notification.institutionName}}"})}
.notification-close
button(ng-click="dismiss(notification)").close.pull-right
span(aria-hidden="true") &times;
span.sr-only #{translate("close")}
.alert.alert-warning(
ng-switch-when="notification_institution_sso_non_canonical"
)
.notification-body
div
i.fa.fa-fw.fa-exclamation-triangle(aria-hidden="true")
| !{translate("tried_to_log_in_with_email", {email: "{{notification.requestedEmail}}"})} !{translate("in_order_to_match_institutional_metadata_associated", {email: "{{notification.institutionEmail}}"})}
.notification-close
button(ng-click="dismiss(notification)").close.pull-right
span(aria-hidden="true") &times;
span.sr-only #{translate("close")}
.alert.alert-info(
ng-switch-when="notification_institution_sso_already_registered"
)
.notification-body
| !{translate("tried_to_register_with_email", {appName: settings.appName, email: "{{notification.email}}"})}
| #{translate("we_logged_you_in")}
.notification-action
a.btn.btn-sm.btn-info(href="/learn/how-to/Institutional_Login")
| #{translate("find_out_more")}
.notification-close
button(ng-click="dismiss(notification)").close.pull-right
span(aria-hidden="true") &times;
span.sr-only #{translate("close")}
.alert.alert-danger(ng-switch-when="notification_institution_sso_error")
.notification-body
div
i.fa.fa-fw.fa-exclamation-triangle(aria-hidden="true")
| &nbsp;#{translate("generic_something_went_wrong")}.
div(ng-if="notification.error.translatedMessage" ng-bind-html="notification.error.translatedMessage")
div(ng-else="notification.error.message") {{ notification.error.message}}
div(ng-if="notification.error.tryAgain") #{translate("try_again")}.
.notification-close
button(ng-click="dismiss(notification)").close.pull-right
span(aria-hidden="true") &times;
span.sr-only #{translate("close")}
ul.list-unstyled(
ng-controller="EmailNotificationController",
ng-cloak
)
li.notification-entry(
ng-repeat="userEmail in userEmails",
ng-if="showConfirmEmail(userEmail) && projects.length > 0"
)
.alert.alert-warning
.notification-body
div(ng-if="!userEmail.confirmationInflight")
| #{translate("please_confirm_email", {emailAddress: "{{ userEmail.email }}"})}
|
a(
href
ng-click="resendConfirmationEmail(userEmail)"
) (#{translate('resend_confirmation_email')})
div(ng-if="userEmail.confirmationInflight")
i.fa.fa-spinner.fa-spin(aria-hidden="true")
|
| #{translate('resending_confirmation_email')}&hellip;
div(ng-if="!userEmail.confirmationInflight && userEmail.error" aria-live="polite")
span(ng-if="userEmail.errorMessage") {{ userEmail.errorMessage }}
span(ng-if="!userEmail.errorMessage") #{translate('generic_something_went_wrong')}
ui.list-unstyled(ng-controller="UserAffiliationsReconfirmController")
li.notification-entry(
ng-repeat="userEmail in allInReconfirmNotificationPeriods"
)
.alert.alert-info()
+reconfirmAffiliationNotification('/project')
li.notification-entry(
ng-repeat="userEmail in userEmails"
ng-if="userEmail.samlIdentifier && userEmail.samlIdentifier.providerId === reconfirmedViaSAML"
)
+reconfirmedAffiliationNotification()
if showGroupsAndEnterpriseBanner
- var eventSegmentation = '{"location": "dashboard-banner", "variant":"' + groupsAndEnterpriseBannerVariant + '" }'
ul.list-unstyled(
ng-controller="GroupsAndEnterpriseBannerController",
ng-cloak
)
li.notification-entry(
ng-if="isVariantValid && !hasDismissedGroupsAndEnterpriseBanner && projects.length > 0"
event-tracking="groups-and-enterprise-banner-prompt"
event-tracking-mb="true"
event-tracking-trigger="load"
event-segmentation=eventSegmentation
)
.alert.alert-info
.notification-body(ng-switch="groupsAndEnterpriseBannerVariant")
span(ng-switch-when="did-you-know") #{translate("did_you_know_that_overleaf_offers")}
span(ng-switch-when="on-premise") Overleaf On-Premises: Does your company want to keep its data within its firewall? Overleaf offers Server Pro, an on-premises solution for companies. Get in touch to learn more.
span(ng-switch-when="people") Other people at your company may already be using Overleaf. Save money with Overleaf group and company-wide subscriptions. Request more information.
span(ng-switch-when="FOMO") Why do Fortune 500 companies and top research institutions trust Overleaf to streamline their collaboration? Get in touch to learn more.
.notification-action
a.pull-right.btn.btn-sm.btn-info(
href="/for/contact-sales{{urlVariantSuffix}}"
target="_blank"
event-tracking="groups-and-enterprise-banner-click"
event-tracking-mb="true"
event-tracking-trigger="click"
event-segmentation=eventSegmentation
) #{translate("contact_sales")}
.notification-close
button(ng-click="dismiss()").close.pull-right
span(aria-hidden="true") &times;
span.sr-only #{translate("close")}

View file

@ -1,223 +0,0 @@
include ./_current_plan_mixins
.row
.col-xs-12(ng-cloak)
form.project-search.form-horizontal(role="form")
.form-group.has-feedback.has-feedback-left.col-md-7.col-xs-12
input.form-control.col-md-7.col-xs-12(
placeholder=translate('search_projects')+"…",
aria-label=translate('search_projects')+"…",
autofocus='autofocus',
ng-model="searchText.value",
focus-on='search:clear',
ng-keyup="searchProjects()"
)
i.fa.fa-search.form-control-feedback-left(aria-hidden="true")
i.fa.fa-times.form-control-feedback(
ng-click="clearSearchText()",
style="cursor: pointer;",
ng-show="searchText.value.length > 0"
aria-hidden="true"
)
button.sr-only(
type="button"
ng-click="clearSearchText()"
ng-show="searchText.value.length > 0"
) #{translate('clear_search')}
.project-tools(ng-cloak)
.project-list-upgrade-prompt(ng-cloak ng-hide="selectedProjects.length > 0")
+current_plan()
.btn-toolbar
.btn-group(ng-hide="selectedProjects.length < 1")
a.btn.btn-secondary(
href,
aria-label=translate('download'),
tooltip=translate('download'),
tooltip-placement="bottom",
tooltip-append-to-body="true",
ng-click="downloadSelectedProjects()"
)
i.fa.fa-cloud-download(aria-hidden="true")
a.btn.btn-secondary(
href,
ng-if="filter !== 'archived'"
aria-label=translate("archive"),
tooltip=translate("archive"),
tooltip-placement="bottom",
tooltip-append-to-body="true",
ng-click="openArchiveProjectsModal()"
)
i.fa.fa-inbox(aria-hidden="true")
a.btn.btn-secondary(
href,
ng-if="filter !== 'trashed'"
aria-label=translate("trash"),
tooltip=translate("trash"),
tooltip-placement="bottom",
tooltip-append-to-body="true",
ng-click="openTrashProjectsModal()"
)
i.fa.fa-trash(aria-hidden="true")
.btn-group.dropdown(
ng-hide="selectedProjects.length < 1 || filter === 'archived' || filter === 'trashed'",
dropdown
)
a.btn.btn-secondary.dropdown-toggle(
href,
data-toggle="dropdown",
dropdown-toggle,
tooltip=translate('add_to_folders'),
tooltip-append-to-body="true",
tooltip-placement="bottom"
)
i.fa.fa-folder-open
|
span.caret
span.sr-only #{translate('add_to_folders')}
ul.dropdown-menu.dropdown-menu-right.js-tags-dropdown-menu.tags-dropdown-menu(
role="menu"
ng-controller="TagListController"
)
li.dropdown-header #{translate("add_to_folder")}
li(
ng-repeat="tag in tags | orderBy:'name'",
ng-controller="TagDropdownItemController"
)
a(href="#", ng-click="addOrRemoveProjectsFromTag()", stop-propagation="click")
i.fa(
ng-class="{\
'fa-check-square-o': areSelectedProjectsInTag == true,\
'fa-square-o': areSelectedProjectsInTag == false,\
'fa-minus-square-o': areSelectedProjectsInTag == 'partial'\
}"
)
span.sr-only Add or remove project from tag
| {{tag.name}}
li.divider
li
a(href, ng-click="openNewTagModal()", stop-propagation="click") #{translate("create_new_folder")}
.btn-group.dropdown(
ng-hide="selectedProjects.length != 1 || filter === 'archived' || filter === 'trashed'",
dropdown
)
a.btn.btn-secondary.dropdown-toggle(
href,
data-toggle="dropdown",
dropdown-toggle
) #{translate("more")}
span.caret
ul.dropdown-menu.dropdown-menu-right(role="menu")
li(ng-show="getFirstSelectedProject().accessLevel == 'owner'")
a(
href,
ng-click="openRenameProjectModal()"
) #{translate("rename")}
li
a(
href,
ng-click="openCloneProjectModal(getFirstSelectedProject())"
) #{translate("make_copy")}
.btn-group(ng-show="filter === 'archived' && selectedProjects.length > 0")
a.btn.btn-secondary(
href,
data-original-title=translate("unarchive"),
data-toggle="tooltip",
data-placement="bottom",
ng-click="unarchiveProjects(selectedProjects)"
) #{translate("unarchive")}
.btn-group(ng-show="filter === 'trashed' && selectedProjects.length > 0")
a.btn.btn-secondary(
href,
data-original-title=translate("untrash"),
data-toggle="tooltip",
data-placement="bottom",
ng-click="untrashProjects(selectedProjects)"
) #{translate("untrash")}
.btn-group(ng-show="filter === 'trashed' && selectedProjects.length > 0")
a.btn.btn-danger(
href,
ng-if="hasLeavableProjectsSelected() && !hasDeletableProjectsSelected()",
data-original-title=translate('leave'),
data-toggle="tooltip",
data-placement="bottom",
ng-click="openLeaveProjectsModal()"
) #{translate("leave")}
a.btn.btn-danger(
href,
ng-if="hasDeletableProjectsSelected() && !hasLeavableProjectsSelected()",
data-original-title=translate('delete'),
data-toggle="tooltip",
data-placement="bottom",
ng-click="openDeleteProjectsModal()"
) #{translate("delete")}
a.btn.btn-danger(
href,
ng-if="hasDeletableProjectsSelected() && hasLeavableProjectsSelected()",
data-original-title=translate('delete_and_leave'),
data-toggle="tooltip",
data-placement="bottom",
ng-click="openLeaveOrDeleteProjectsModal()"
) #{translate("delete_and_leave")}
.row.row-spaced
.col-xs-12
.card.card-thin.project-list-card
ul.list-unstyled.project-list.structured-list(
select-all-list,
ng-if="projects.length > 0",
max-height="projectListHeight - 25",
ng-cloak
)
table.project-list-table
tr.project-list-table-header-row
th.project-list-table-name-cell
.project-list-table-name-container
input.project-list-table-select-item(
select-all,
type="checkbox"
aria-label=translate('select_all_projects')
)
span.header.clickable.project-list-table-name(ng-click="changePredicate('name')") #{translate("title")}
i.tablesort.fa(ng-class="getSortIconClass('name')" aria-hidden="true")
button.sr-only(ng-click="changePredicate('name')") Sort by #{translate("title")}
th.project-list-table-owner-cell
span.header.clickable(ng-click="changePredicate('ownerName')") #{translate("owner")}
i.tablesort.fa(ng-class="getSortIconClass('ownerName')" aria-hidden="true")
button.sr-only(ng-click="changePredicate('ownerName')") Sort by #{translate("owner")}
th.project-list-table-lastupdated-cell
span.header.clickable(ng-click="changePredicate('lastUpdated')") #{translate("last_modified")}
i.tablesort.fa(ng-class="getSortIconClass('lastUpdated')" aria-hidden="true")
button.sr-only(ng-click="changePredicate('lastUpdated')") Sort by #{translate("last_modified")}
th.project-list-table-actions-cell
span.header #{translate("actions")}
tr.project-list-table-row(
ng-repeat="project in visibleProjects | orderBy:getValueForCurrentPredicate:reverse:comparator",
ng-controller="ProjectListItemController"
select-row
)
include ./item
tr(
ng-if="visibleProjects.length == 0",
ng-cloak
)
td(colspan="4").project-list-table-no-projects-cell
span.small #{translate("no_projects")}
div.welcome.text-centered(ng-if="projects.length == 0", ng-cloak)
h2 #{translate("welcome_to_sl")}
p #{translate("new_to_latex_look_at")}
a(href="/templates") #{translate("templates").toLowerCase()}
| #{translate("or")}
a(href="/learn") #{translate("latex_help_guide")}
| ,
br
| #{translate("or_create_project_left")}

View file

@ -1,135 +0,0 @@
.dropdown(
dropdown
dropdown-append-to-body
)
a.btn.btn-primary.sidebar-new-proj-btn.dropdown-toggle(
href="#",
data-toggle="dropdown",
dropdown-toggle
)
| #{translate("new_project")}
ul.dropdown-menu(role="menu")
li
a(
href,
ng-click="openCreateProjectModal()"
) #{translate("blank_project")}
li
a(
href,
ng-click="openCreateProjectModal('example')"
) #{translate("example_project")}
li
a(
href,
ng-click="openUploadProjectModal()"
) #{translate("upload_project")}
!= moduleIncludes("newProjectMenu", locals)
if portalTemplates.length > 0
//- portalTemplates is set in ProjectController
li.divider
li.dropdown-header #{translate("institution")} #{translate("templates")}
for portal in portalTemplates
li
a.menu-indent(
href=portal.url + "#templates",
ng-non-bindable
) #{portal.name}
if (templates)
//- templates is an express local var, via settings.templateLinks
li.divider
li.dropdown-header #{translate("templates")}
each item in templates
li
a.menu-indent(href=item.url) #{translate(item.name)}
.row-spaced(ng-if="projects.length > 0", ng-cloak)
ul.list-unstyled.folders-menu(
ng-controller="TagListController"
)
li(ng-class="{active: (filter == 'all')}", ng-click="filterProjects('all')")
a(href) #{translate("all_projects")}
li(ng-class="{active: (filter == 'owned')}", ng-click="filterProjects('owned')")
a(href) #{translate("your_projects")}
li(ng-class="{active: (filter == 'shared')}", ng-click="filterProjects('shared')")
a(href) #{translate("shared_with_you")}
li(ng-class="{active: (filter == 'archived')}", ng-click="filterProjects('archived')")
a(href) #{translate("archived_projects")}
li(ng-class="{active: (filter == 'trashed')}", ng-click="filterProjects('trashed')")
a(href) #{translate("trashed_projects")}
li.separator
h2 #{translate("tags_slash_folders")}
li.tag(ng-cloak)
a.tag-name(href, ng-click="openNewTagModal()")
i.fa.fa-fw.fa-plus(aria-hidden="true")
span.name #{translate("new_folder")}
li.tag(
ng-repeat="tag in tags | orderBy:'name'",
ng-class="{active: tag.selected}",
ng-cloak,
ng-click="selectTag(tag)"
)
a.tag-name(href)
i.icon.fa.fa-fw(
ng-class="{\
'fa-folder-open': tag.selected,\
'fa-folder': !tag.selected\
}"
ng-style="{ 'color': 'hsl({{ getHueForTagId(tag._id) }}, 70%, 45%)' }"
aria-hidden="true"
)
span.name {{tag.name}}
span.subdued ({{countProjectsForTag(tag)}})
span.dropdown.tag-menu(dropdown)
a.dropdown-toggle(
href="#",
data-toggle="dropdown",
dropdown-toggle,
stop-propagation="click"
)
span.caret
ul.dropdown-menu.dropdown-menu-right(
role="menu"
)
li
a(href, ng-click="renameTag(tag)", stop-propagation="click")
| #{translate("rename")}
li
a(href, ng-click="deleteTag(tag)", stop-propagation="click")
| #{translate("delete")}
li.tag.untagged(
ng-if="tags.length",
ng-cloak,
ng-click="selectUntagged()"
ng-class="{active: filter === 'untagged'}",
)
a.tag-name(href)
span.name
| #{translate("uncategorized")}
span.subdued ({{ nUntagged }})
.row-spaced(ng-if="projects.length == 0", ng-cloak)
.first-project
div
i.fa.fa-arrow-up.fa-2x(aria-hidden="true")
div
strong #{translate("create_your_first_project")}
if (isOverleaf)
span(ng-controller="LeftHandMenuPromoController", ng-cloak)
.row-spaced#userProfileInformation(ng-if="hasProjects")
div(ng-hide="withAffiliations", ng-cloak)
hr
.text-centered.add-affiliation
p Are you affiliated with an institution?
a.btn.btn-secondary-info.btn-secondary(
href="/user/settings"
) Add Affiliation

View file

@ -1,8 +0,0 @@
if (typeof(suggestedLanguageSubdomainConfig) != "undefined")
span(ng-controller="TranslationsPopupController", ng-cloak)
.translations-message(ng-hide="hidei18nNotification")
a(href=suggestedLanguageSubdomainConfig.url+currentUrl) !{translate("click_here_to_view_sl_in_lng", {lngName: translate(suggestedLanguageSubdomainConfig.lngCode)}, ['strong'])}
img(src=buildImgPath("flags/24/" + suggestedLanguageSubdomainConfig.lngCode + ".png"))
button(ng-click="dismiss()").close.pull-right
span(aria-hidden="true") &times;
span.sr-only #{translate("close")}

View file

@ -820,7 +820,7 @@ module.exports = {
reportOnly: process.env.CSP_REPORT_ONLY === 'true',
reportPercentage: parseFloat(process.env.CSP_REPORT_PERCENTAGE) || 0,
reportUri: process.env.CSP_REPORT_URI,
exclude: ['app/views/project/editor', 'app/views/project/list'],
exclude: ['app/views/project/editor', 'app/views/project/list-react'],
},
unsupportedBrowsers: {

View file

@ -1,8 +1,5 @@
// TODO: This file was created by bulk-decaffeinate.
// Fix any style issues and re-enable lint.
import './project-list'
import './modal-controllers'
import './tag-controllers'
import './notifications-controller'
import './left-hand-menu-promo-controller'
import '../../services/queued-http'

View file

@ -1,14 +0,0 @@
import App from '../../base'
export default App.controller(
'LeftHandMenuPromoController',
function ($scope, UserAffiliationsDataService, eventTracking) {
$scope.hasProjects = window.data.projects.length > 0
const _userHasNoAffiliation = function () {
$scope.withAffiliations = window.data.userAffiliations.length > 0
}
_userHasNoAffiliation()
}
)

View file

@ -12,45 +12,6 @@
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
import App from '../../base'
App.controller(
'RenameProjectModalController',
function ($scope, $modalInstance, $timeout, project, queuedHttp) {
$scope.inputs = { projectName: project.name }
$scope.state = {
inflight: false,
error: false,
}
$modalInstance.opened.then(() =>
$timeout(() => $scope.$broadcast('open'), 200)
)
$scope.rename = function () {
$scope.state.inflight = true
$scope.state.error = false
return $scope
.renameProject(project, $scope.inputs.projectName)
.then(function () {
$scope.state.inflight = false
$scope.state.error = false
return $modalInstance.close()
})
.catch(function (response) {
const { data, status } = response
$scope.state.inflight = false
if (status === 400) {
return ($scope.state.error = { message: data })
} else {
return ($scope.state.error = true)
}
})
}
return ($scope.cancel = () => $modalInstance.dismiss('cancel'))
}
)
App.controller(
'CloneProjectModalController',
function ($scope, $modalInstance, $timeout, project) {
@ -88,45 +49,6 @@ App.controller(
}
)
App.controller(
'NewProjectModalController',
function ($scope, $modalInstance, $timeout, template) {
$scope.inputs = { projectName: '' }
$scope.state = {
inflight: false,
error: false,
}
$modalInstance.opened.then(() =>
$timeout(() => $scope.$broadcast('open'), 200)
)
$scope.create = function () {
$scope.state.inflight = true
$scope.state.error = false
return $scope
.createProject($scope.inputs.projectName, template)
.then(function (response) {
const { data } = response
$scope.state.inflight = false
$scope.state.error = false
return $modalInstance.close(data.project_id)
})
.catch(function (response) {
const { data, status } = response
$scope.state.inflight = false
if (status === 400) {
return ($scope.state.error = { message: data })
} else {
return ($scope.state.error = true)
}
})
}
return ($scope.cancel = () => $modalInstance.dismiss('cancel'))
}
)
App.controller(
'ArchiveTrashLeaveOrDeleteProjectsModalController',
function ($scope, $modalInstance, $timeout, projects, action) {
@ -139,33 +61,3 @@ App.controller(
$scope.cancel = () => $modalInstance.dismiss('cancel')
}
)
App.controller(
'UploadProjectModalController',
function ($scope, $modalInstance, $timeout) {
$scope.cancel = () => $modalInstance.dismiss('cancel')
return ($scope.onComplete = function (error, name, response) {
if (response.project_id != null) {
return (window.location = `/project/${response.project_id}`)
}
})
}
)
App.controller(
'V1ImportModalController',
function ($scope, $modalInstance, project) {
$scope.project = project
return ($scope.dismiss = () => $modalInstance.dismiss('cancel'))
}
)
export default App.controller(
'ShowErrorModalController',
function ($scope, $modalInstance, error) {
$scope.error = error
return ($scope.cancel = () => $modalInstance.dismiss('cancel'))
}
)

View file

@ -1,141 +0,0 @@
import App from '../../base'
import getMeta from '../../utils/meta'
const ExposedSettings = window.ExposedSettings
App.controller('NotificationsController', function ($scope, $http) {
for (const notification of $scope.notifications || []) {
notification.hide = false
}
$scope.samlInitPath = ExposedSettings.samlInitPath
$scope.dismiss = notification => {
if (!notification._id) {
notification.hide = true
return
}
$http({
url: `/notifications/${notification._id}`,
method: 'DELETE',
headers: {
'X-Csrf-Token': window.csrfToken,
},
}).then(() => (notification.hide = true))
}
})
App.controller(
'GroupsAndEnterpriseBannerController',
function ($scope, localStorage) {
$scope.hasDismissedGroupsAndEnterpriseBanner = localStorage(
'has_dismissed_groups_and_enterprise_banner'
)
$scope.dismiss = () => {
localStorage('has_dismissed_groups_and_enterprise_banner', true)
$scope.hasDismissedGroupsAndEnterpriseBanner = true
}
$scope.groupsAndEnterpriseBannerVariant = getMeta(
'ol-groupsAndEnterpriseBannerVariant'
)
const valid = ['did-you-know', 'on-premise', 'people', 'FOMO']
$scope.isVariantValid = valid.includes(
$scope.groupsAndEnterpriseBannerVariant
)
$scope.urlVariantSuffix = $scope.isVariantValid
? `-${valid.indexOf($scope.groupsAndEnterpriseBannerVariant) + 1}`
: ''
}
)
App.controller('ProjectInviteNotificationController', function ($scope, $http) {
// Shortcuts for translation keys
$scope.projectName = $scope.notification.messageOpts.projectName
$scope.userName = $scope.notification.messageOpts.userName
$scope.accept = function () {
$scope.notification.inflight = true
return $http({
url: `/project/${$scope.notification.messageOpts.projectId}/invite/token/${$scope.notification.messageOpts.token}/accept`,
method: 'POST',
headers: {
'X-Csrf-Token': window.csrfToken,
'X-Requested-With': 'XMLHttpRequest',
},
})
.then(() => {
$scope.notification.accepted = true
})
.catch(({ status }) => {
if (status === 404) {
// 404 probably means the invite has already been accepted and
// deleted. Treat as success
$scope.notification.accepted = true
} else {
$scope.notification.error = true
}
})
.finally(() => {
$scope.notification.inflight = false
})
}
})
App.controller(
'EmailNotificationController',
function ($scope, $http, UserAffiliationsDataService) {
$scope.userEmails = window.data.userEmails
const _ssoAvailable = email => {
if (!ExposedSettings.hasSamlFeature) return false
if (email.samlProviderId) return true
if (!email.affiliation || !email.affiliation.institution) return false
if (email.affiliation.institution.ssoEnabled) return true
if (
ExposedSettings.hasSamlBeta &&
email.affiliation.institution.ssoBeta
) {
return true
}
return false
}
$scope.showConfirmEmail = email => {
if (ExposedSettings.emailConfirmationDisabled) {
return false
}
if (!email.confirmedAt && !email.hide) {
if (_ssoAvailable(email)) {
return false
}
return true
}
return false
}
for (const userEmail of $scope.userEmails) {
userEmail.hide = false
}
$scope.resendConfirmationEmail = function (userEmail) {
userEmail.confirmationInflight = true
userEmail.error = false
userEmail.errorMessage = null
UserAffiliationsDataService.resendConfirmationEmail(userEmail.email)
.then(() => {
userEmail.hide = true
$scope.$emit('project-list:notifications-received')
})
.catch(error => {
userEmail.error = true
userEmail.errorMessage = error.data.message
console.error(error)
$scope.$emit('project-list:notifications-received')
})
.finally(() => {
userEmail.confirmationInflight = false
})
}
}
)

View file

@ -1,949 +0,0 @@
import _ from 'lodash'
import App from '../../base'
import './services/project-list'
import getMeta from '../../utils/meta'
App.controller(
'ProjectPageController',
function (
$scope,
$modal,
$window,
queuedHttp,
eventTracking, // eslint-disable-line camelcase
$timeout,
localStorage,
ProjectListService
) {
$scope.projects = window.data.projects
$scope.tags = window.data.tags
$scope.notifications = window.data.notifications
$scope.notificationsInstitution = window.data.notificationsInstitution
$scope.allSelected = false
$scope.selectedProjects = []
$scope.filter = 'all'
$scope.predicate = 'lastUpdated'
$scope.nUntagged = 0
$scope.reverse = true
$scope.searchText = { value: '' }
$scope.$watch('predicate', function (newValue) {
$scope.comparator =
newValue === 'ownerName' ? ownerNameComparator : defaultComparator
})
const surveyName = getMeta('ol-survey-name')
$scope.shouldShowSurveyLink =
localStorage(`dismissed-${surveyName}`) !== true
$scope.dismissSurvey = () => {
localStorage(`dismissed-${surveyName}`, true)
$scope.shouldShowSurveyLink = false
}
$timeout(() => recalculateProjectListHeight(), 10)
$scope.$watch(
() =>
$scope.projects.filter(
project =>
(project.tags == null || project.tags.length === 0) &&
!project.archived &&
!project.trashed
).length,
newVal => ($scope.nUntagged = newVal)
)
function recalculateProjectListHeight() {
const $projListCard = $('.project-list-card')
if (!$projListCard || !$projListCard.offset()) return
const topOffset = $projListCard.offset().top
const cardPadding = $projListCard.outerHeight() - $projListCard.height()
const bottomOffset = $('footer').outerHeight()
const height =
$window.innerHeight - topOffset - bottomOffset - cardPadding
$scope.projectListHeight = height
}
function defaultComparator(v1, v2) {
let result = 0
const type1 = v1.type
const type2 = v2.type
if ($scope.predicate === 'ownerName') {
return
}
if (type1 === type2) {
let value1 = v1.value
let value2 = v2.value
if (type1 === 'string') {
// Compare strings case-insensitively
value1 = value1.toLowerCase()
value2 = value2.toLowerCase()
} else if (type1 === 'object') {
// For basic objects, use the position of the object
// in the collection instead of the value
if (angular.isObject(value1)) value1 = v1.index
if (angular.isObject(value2)) value2 = v2.index
}
if (value1 !== value2) {
result = value1 < value2 ? -1 : 1
}
} else {
result = type1 < type2 ? -1 : 1
}
return result
}
function ownerNameComparator(v1, v2) {
if ($scope.predicate !== 'ownerName') {
return
}
if (v1.value === 'You') {
if (v2.value === 'You') {
return v1.index < v2.index ? -1 : 1
} else {
return 1
}
} else if (v1.value === 'An Overleaf v1 User' || v1.value === 'None') {
if (v2.value === 'An Overleaf v1 User' || v2.value === 'None') {
return v1.index < v2.index ? -1 : 1
} else {
return -1
}
} else {
if (v2.value === 'You') {
return -1
} else if (v2.value === 'An Overleaf v1 User' || v2.value === 'None') {
return 1
} else {
return v1.value > v2.value ? -1 : 1
}
}
}
angular.element($window).bind('resize', function () {
recalculateProjectListHeight()
$scope.$apply()
})
$scope.$on('project-list:notifications-received', () =>
$scope.$applyAsync(() => recalculateProjectListHeight())
)
// Allow tags to be accessed on projects as well
const projectsById = {}
for (const project of $scope.projects) {
projectsById[project.id] = project
}
$scope.getProjectById = id => projectsById[id]
for (const tag of $scope.tags) {
for (const projectId of tag.project_ids || []) {
const project = projectsById[projectId]
if (project) {
if (!project.tags) {
project.tags = []
}
project.tags.push(tag)
}
}
}
$scope.changePredicate = function (newPredicate) {
if ($scope.predicate === newPredicate) {
$scope.reverse = !$scope.reverse
}
$scope.predicate = newPredicate
}
$scope.getSortIconClass = function (column) {
if (column === $scope.predicate && $scope.reverse) {
return 'fa-caret-down'
} else if (column === $scope.predicate && !$scope.reverse) {
return 'fa-caret-up'
} else {
return ''
}
}
$scope.searchProjects = function () {
eventTracking.send(
'project-list-page-interaction',
'project-search',
'keydown'
)
$scope.updateVisibleProjects()
}
$scope.clearSearchText = function () {
$scope.searchText.value = ''
$scope.filter = 'all'
$scope.$emit('search:clear')
$scope.updateVisibleProjects()
}
$scope.setFilter = function (filter) {
$scope.filter = filter
$scope.updateVisibleProjects()
}
$scope.updateSelectedProjects = function () {
$scope.selectedProjects = $scope.projects.filter(
project => project.selected
)
}
$scope.getSelectedProjects = () => $scope.selectedProjects
$scope.getSelectedProjectIds = () =>
$scope.selectedProjects.map(project => project.id)
$scope.getFirstSelectedProject = () => $scope.selectedProjects[0]
$scope.hasLeavableProjectsSelected = () =>
_.some(
$scope.getSelectedProjects(),
project => project.accessLevel !== 'owner' && project.trashed
)
$scope.hasDeletableProjectsSelected = () =>
_.some(
$scope.getSelectedProjects(),
project => project.accessLevel === 'owner' && project.trashed
)
$scope.updateVisibleProjects = function () {
$scope.visibleProjects = []
const selectedTag = $scope.getSelectedTag()
for (const project of $scope.projects) {
let visible = true
// Only show if it matches any search text
if ($scope.searchText.value !== '') {
if (
project.name
.toLowerCase()
.indexOf($scope.searchText.value.toLowerCase()) === -1
) {
visible = false
}
}
// Only show if it matches the selected tag
if (
$scope.filter === 'tag' &&
selectedTag != null &&
!selectedTag.project_ids.includes(project.id)
) {
visible = false
}
// Hide tagged projects if we only want to see the uncategorized ones
if (
$scope.filter === 'untagged' &&
(project.tags != null ? project.tags.length : undefined) > 0
) {
visible = false
}
// Hide projects we own if we only want to see shared projects
if ($scope.filter === 'shared' && project.accessLevel === 'owner') {
visible = false
}
// Hide projects we don't own if we only want to see owned projects
if ($scope.filter === 'owned' && project.accessLevel !== 'owner') {
visible = false
}
if ($scope.filter === 'archived') {
// Only show archived projects
if (!project.archived) {
visible = false
}
} else {
// Only show non-archived projects
if (project.archived) {
visible = false
}
}
if ($scope.filter === 'trashed') {
// Only show trashed projects
if (!project.trashed) {
visible = false
}
} else {
// Only show non-trashed projects
if (project.trashed) {
visible = false
}
}
if (visible) {
$scope.visibleProjects.push(project)
} else {
// We don't want hidden selections
project.selected = false
}
}
localStorage(
'project_list',
JSON.stringify({
filter: $scope.filter,
selectedTagId: selectedTag != null ? selectedTag._id : undefined,
})
)
$scope.updateSelectedProjects()
}
$scope.getSelectedTag = function () {
for (const tag of $scope.tags) {
if (tag.selected) {
return tag
}
}
return null
}
$scope._removeProjectIdsFromTagArray = function (tag, removeProjectIds) {
// Remove project_id from tag.project_ids
const remainingProjectIds = []
const removedProjectIds = []
for (const projectId of tag.project_ids) {
if (!removeProjectIds.includes(projectId)) {
remainingProjectIds.push(projectId)
} else {
removedProjectIds.push(projectId)
}
}
tag.project_ids = remainingProjectIds
return removedProjectIds
}
$scope._removeProjectFromList = function (project) {
const index = $scope.projects.indexOf(project)
if (index > -1) {
$scope.projects.splice(index, 1)
}
}
$scope.removeSelectedProjectsFromTag = function (tag) {
tag.showWhenEmpty = true
const selectedProjectIds = $scope.getSelectedProjectIds()
const selectedProjects = $scope.getSelectedProjects()
const removedProjectIds = $scope._removeProjectIdsFromTagArray(
tag,
selectedProjectIds
)
// Remove tag from project.tags
for (const project of selectedProjects) {
if (!project.tags) {
project.tags = []
}
const index = project.tags.indexOf(tag)
if (index > -1) {
project.tags.splice(index, 1)
}
}
for (const projectId of removedProjectIds) {
queuedHttp({
method: 'DELETE',
url: `/tag/${tag._id}/project/${projectId}`,
headers: {
'X-CSRF-Token': window.csrfToken,
},
})
}
// If we're filtering by this tag then we need to remove
// the projects from view
$scope.updateVisibleProjects()
}
$scope.removeProjectFromTag = function (project, tag) {
tag.showWhenEmpty = true
if (!project.tags) {
project.tags = []
}
const index = project.tags.indexOf(tag)
if (index > -1) {
$scope._removeProjectIdsFromTagArray(tag, [project.id])
project.tags.splice(index, 1)
queuedHttp({
method: 'DELETE',
url: `/tag/${tag._id}/project/${project.id}`,
headers: {
'X-CSRF-Token': window.csrfToken,
},
})
$scope.updateVisibleProjects()
}
}
$scope.addSelectedProjectsToTag = function (tag) {
const selectedProjects = $scope.getSelectedProjects()
eventTracking.send(
'project-list-page-interaction',
'project action',
'addSelectedProjectsToTag'
)
// Add project_ids into tag.project_ids
const addedProjectIds = []
for (const projectId of $scope.getSelectedProjectIds()) {
if (!tag.project_ids.includes(projectId)) {
tag.project_ids.push(projectId)
addedProjectIds.push(projectId)
}
}
// Add tag into each project.tags
for (const project of selectedProjects) {
if (!project.tags) {
project.tags = []
}
if (!project.tags.includes(tag)) {
project.tags.push(tag)
}
}
for (const projectId of addedProjectIds) {
queuedHttp.post(`/tag/${tag._id}/project/${projectId}`, {
_csrf: window.csrfToken,
})
}
}
$scope.openNewTagModal = function (e) {
const modalInstance = $modal.open({
templateUrl: 'newTagModalTemplate',
controller: 'NewTagModalController',
})
modalInstance.result.then(function (tag) {
const tagIsDuplicate = $scope.tags.find(function (existingTag) {
return tag.name === existingTag.name
})
if (!tagIsDuplicate) {
$scope.tags.push(tag)
$scope.addSelectedProjectsToTag(tag)
}
})
}
$scope.createProject = function (name, template) {
if (template == null) {
template = 'none'
}
return queuedHttp
.post('/project/new', {
_csrf: window.csrfToken,
projectName: name,
template,
})
.then(function (response) {
const { data } = response
$scope.projects.push({
name,
id: data.project_id,
accessLevel: 'owner',
owner: data.owner,
// TODO: Check access level if correct after adding it in
// to the rest of the app
})
$scope.updateVisibleProjects()
})
}
$scope.openCreateProjectModal = function (template) {
if (template == null) {
template = 'none'
}
eventTracking.send(
'project-list-page-interaction',
'new-project',
template
)
const modalInstance = $modal.open({
templateUrl: 'newProjectModalTemplate',
controller: 'NewProjectModalController',
resolve: {
template() {
return template
},
},
scope: $scope,
})
modalInstance.result.then(
projectId => (window.location = `/project/${projectId}`)
)
}
$scope.renameProject = (project, newName) =>
queuedHttp
.post(`/project/${project.id}/rename`, {
newProjectName: newName,
_csrf: window.csrfToken,
})
.then(() => (project.name = newName))
$scope.openRenameProjectModal = function () {
const project = $scope.getFirstSelectedProject()
if (!project || project.accessLevel !== 'owner') {
return
}
eventTracking.send(
'project-list-page-interaction',
'project action',
'Rename'
)
$modal.open({
templateUrl: 'renameProjectModalTemplate',
controller: 'RenameProjectModalController',
resolve: {
project() {
return project
},
},
scope: $scope,
})
}
$scope.cloneProject = function (project, cloneName) {
eventTracking.send(
'project-list-page-interaction',
'project action',
'Clone'
)
return queuedHttp
.post(`/project/${project.id}/clone`, {
_csrf: window.csrfToken,
projectName: cloneName,
})
.then(function (response) {
const { data } = response
$scope.projects.push({
name: data.name,
id: data.project_id,
accessLevel: 'owner',
owner: data.owner,
// TODO: Check access level if correct after adding it in
// to the rest of the app
})
$scope.updateVisibleProjects()
})
}
$scope.openCloneProjectModal = function (project) {
if (!project) {
return
}
$modal.open({
templateUrl: 'cloneProjectModalTemplate',
controller: 'CloneProjectModalController',
resolve: {
project() {
return project
},
},
scope: $scope,
})
}
// Methods to create modals for archiving, trashing, leaving and deleting projects
const _createArchiveTrashLeaveOrDeleteProjectsModal = function (
action,
projects
) {
eventTracking.send(
'project-list-page-interaction',
'project action',
action
)
return $modal.open({
templateUrl: 'archiveTrashLeaveOrDeleteProjectsModalTemplate',
controller: 'ArchiveTrashLeaveOrDeleteProjectsModalController',
resolve: {
projects() {
return projects
},
action() {
return action
},
},
})
}
$scope.createArchiveProjectsModal = function (projects) {
return _createArchiveTrashLeaveOrDeleteProjectsModal('archive', projects)
}
$scope.createTrashProjectsModal = function (projects) {
return _createArchiveTrashLeaveOrDeleteProjectsModal('trash', projects)
}
$scope.createLeaveProjectsModal = function (projects) {
return _createArchiveTrashLeaveOrDeleteProjectsModal('leave', projects)
}
$scope.createDeleteProjectsModal = function (projects) {
return _createArchiveTrashLeaveOrDeleteProjectsModal('delete', projects)
}
$scope.createLeaveOrDeleteProjectsModal = function (projects) {
return _createArchiveTrashLeaveOrDeleteProjectsModal(
'leaveOrDelete',
projects
)
}
//
$scope.openArchiveProjectsModal = function () {
const modalInstance = $scope.createArchiveProjectsModal(
$scope.getSelectedProjects()
)
modalInstance.result.then(() => $scope.archiveSelectedProjects())
}
$scope.openTrashProjectsModal = function () {
const modalInstance = $scope.createTrashProjectsModal(
$scope.getSelectedProjects()
)
modalInstance.result.then(() => $scope.trashSelectedProjects())
}
$scope.openLeaveProjectsModal = function () {
const modalInstance = $scope.createLeaveProjectsModal(
$scope.getSelectedProjects()
)
modalInstance.result.then(() => $scope.leaveSelectedProjects())
}
$scope.openDeleteProjectsModal = function () {
const modalInstance = $scope.createDeleteProjectsModal(
$scope.getSelectedProjects()
)
modalInstance.result.then(() => $scope.deleteSelectedProjects())
}
$scope.openLeaveOrDeleteProjectsModal = function () {
const modalInstance = $scope.createLeaveOrDeleteProjectsModal(
$scope.getSelectedProjects()
)
modalInstance.result.then(() => $scope.leaveOrDeleteSelectedProjects())
}
//
$scope.archiveSelectedProjects = () =>
$scope.archiveProjects($scope.getSelectedProjects())
$scope.unarchiveSelectedProjects = () =>
$scope.unarchiveProjects($scope.getSelectedProjects())
$scope.trashSelectedProjects = () =>
$scope.trashProjects($scope.getSelectedProjects())
$scope.untrashSelectedProjects = () =>
$scope.untrashProjects($scope.getSelectedProjects())
$scope.leaveSelectedProjects = () =>
$scope.leaveProjects($scope.getSelectedProjects())
$scope.deleteSelectedProjects = () =>
$scope.deleteProjects($scope.getSelectedProjects())
$scope.leaveOrDeleteSelectedProjects = () =>
$scope.leaveOrDeleteProjects($scope.getSelectedProjects())
//
$scope.archiveProjects = function (projects) {
for (const project of projects) {
project.archived = true
project.trashed = false
_archiveProject(project)
}
$scope.updateVisibleProjects()
}
$scope.unarchiveProjects = function (projects) {
for (const project of projects) {
project.archived = false
_unarchiveProject(project)
}
$scope.updateVisibleProjects()
}
$scope.trashProjects = function (projects) {
for (const project of projects) {
project.trashed = true
project.archived = false
_trashProject(project)
}
$scope.updateVisibleProjects()
}
$scope.untrashProjects = function (projects) {
for (const project of projects) {
project.trashed = false
_untrashProject(project)
}
$scope.updateVisibleProjects()
}
$scope.leaveProjects = function (projects) {
_deleteOrLeaveProjectsLocally(projects)
for (const project of projects) {
_leaveProject(project)
}
$scope.updateVisibleProjects()
}
$scope.deleteProjects = function (projects) {
_deleteOrLeaveProjectsLocally(projects)
for (const project of projects) {
_deleteProject(project)
}
$scope.updateVisibleProjects()
}
$scope.leaveOrDeleteProjects = function (projects) {
_deleteOrLeaveProjectsLocally(projects)
for (const project of projects) {
if (project.accessLevel === 'owner') {
_deleteProject(project)
} else {
_leaveProject(project)
}
}
$scope.updateVisibleProjects()
}
// Actual interaction with the backend---we could move this into a service
const _archiveProject = function (project) {
return queuedHttp({
method: 'POST',
url: `/project/${project.id}/archive`,
headers: {
'X-CSRF-Token': window.csrfToken,
},
})
}
const _unarchiveProject = function (project) {
return queuedHttp({
method: 'DELETE',
url: `/project/${project.id}/archive`,
headers: {
'X-CSRF-Token': window.csrfToken,
},
})
}
const _trashProject = function (project) {
return queuedHttp({
method: 'POST',
url: `/project/${project.id}/trash`,
headers: {
'X-CSRF-Token': window.csrfToken,
},
})
}
const _untrashProject = function (project) {
return queuedHttp({
method: 'DELETE',
url: `/project/${project.id}/trash`,
headers: {
'X-CSRF-Token': window.csrfToken,
},
})
}
const _leaveProject = function (project) {
return queuedHttp({
method: 'POST',
url: `/project/${project.id}/leave`,
headers: {
'X-CSRF-Token': window.csrfToken,
},
})
}
const _deleteProject = function (project) {
return queuedHttp({
method: 'DELETE',
url: `/project/${project.id}`,
headers: {
'X-CSRF-Token': window.csrfToken,
},
})
}
const _deleteOrLeaveProjectsLocally = function (projects) {
const projectIds = projects.map(p => p.id)
for (const tag of $scope.tags || []) {
$scope._removeProjectIdsFromTagArray(tag, projectIds)
}
for (const project of projects || []) {
$scope._removeProjectFromList(project)
}
}
$scope.getValueForCurrentPredicate = function (project) {
if ($scope.predicate === 'ownerName') {
return ProjectListService.getOwnerName(project)
} else {
return project[$scope.predicate]
}
}
$scope.openUploadProjectModal = function () {
$modal.open({
templateUrl: 'uploadProjectModalTemplate',
controller: 'UploadProjectModalController',
})
}
$scope.downloadSelectedProjects = () =>
$scope.downloadProjectsById($scope.getSelectedProjectIds())
$scope.sendUpgradeButtonClickEvent = () => {
eventTracking.sendMB('upgrade-button-click', {
source: 'dashboard-top',
'project-dashboard-react': 'default',
'is-dashboard-sidebar-hidden': false,
'is-screen-width-less-than-768px':
window.matchMedia('(max-width: 767px)').matches,
})
}
$scope.downloadProjectsById = function (projectIds) {
let path
eventTracking.send(
'project-list-page-interaction',
'project action',
'Download Zip'
)
if (projectIds.length > 1) {
path = `/project/download/zip?project_ids=${projectIds.join(',')}`
} else {
path = `/project/${projectIds[0]}/download/zip`
}
return (window.location = path)
}
const markTagAsSelected = id => {
for (const tag of $scope.tags) {
if (tag._id === id) {
tag.selected = true
} else {
tag.selected = false
}
}
}
const storedUIOpts = JSON.parse(localStorage('project_list'))
if (storedUIOpts && storedUIOpts.filter) {
if (storedUIOpts.filter === 'tag' && storedUIOpts.selectedTagId) {
markTagAsSelected(storedUIOpts.selectedTagId)
}
$scope.setFilter(storedUIOpts.filter)
} else {
$scope.updateVisibleProjects()
}
}
)
App.controller(
'ProjectListItemController',
function ($scope, $modal, queuedHttp, ProjectListService) {
$scope.projectLink = function (project) {
return `/project/${project.id}`
}
$scope.isLinkSharingProject = project => project.source === 'token'
$scope.hasGenericOwnerName = () => {
/* eslint-disable camelcase */
const { first_name, last_name, email } = $scope.project.owner
return !first_name && !last_name && !email
/* eslint-enable camelcase */
}
$scope.getOwnerName = ProjectListService.getOwnerName
$scope.getUserName = ProjectListService.getUserName
$scope.isOwner = () =>
$scope.project.owner && window.user_id === $scope.project.owner._id
$scope.$watch('project.selected', function (value) {
if (value != null) {
$scope.updateSelectedProjects()
}
})
$scope.clone = function (e) {
e.stopPropagation()
$scope.openCloneProjectModal($scope.project)
}
$scope.download = function (e) {
e.stopPropagation()
$scope.downloadProjectsById([$scope.project.id])
}
$scope.archive = function (e) {
e.stopPropagation()
$scope.createArchiveProjectsModal([$scope.project]).result.then(() => {
$scope.archiveProjects([$scope.project])
})
}
$scope.unarchive = function (e) {
e.stopPropagation()
$scope.unarchiveProjects([$scope.project])
}
$scope.trash = function (e) {
e.stopPropagation()
$scope.createTrashProjectsModal([$scope.project]).result.then(() => {
$scope.trashProjects([$scope.project])
})
}
$scope.untrash = function (e) {
e.stopPropagation()
$scope.untrashProjects([$scope.project])
}
$scope.leave = function (e) {
e.stopPropagation()
$scope.createLeaveProjectsModal([$scope.project]).result.then(() => {
$scope.leaveProjects([$scope.project])
})
}
$scope.delete = function (e) {
e.stopPropagation()
$scope.createDeleteProjectsModal([$scope.project]).result.then(() => {
$scope.deleteProjects([$scope.project])
})
}
}
)

View file

@ -1,35 +0,0 @@
/* eslint-disable
camelcase
*/
import App from '../../../base'
export default App.service('ProjectListService', function () {
return {
getOwnerName(project) {
if (project.accessLevel === 'owner') {
return 'You'
} else if (project.owner != null) {
return this.getUserName(project.owner)
} else {
return 'None'
}
},
getUserName(user) {
if (user && user._id === window.user_id) {
return 'You'
} else if (user) {
const { first_name, last_name, email } = user
if (first_name || last_name) {
return [first_name, last_name].filter(n => n != null).join(' ')
} else if (email) {
return email
} else {
return 'An Overleaf v1 User'
}
} else {
return 'None'
}
},
}
})

View file

@ -11,7 +11,6 @@
"add": "Přidat",
"add_more_members": "Přidat více členů",
"add_to_folder": "Přidat do složky",
"add_to_folders": "Přidat do složek",
"add_your_first_group_member_now": "Přidejte do vaší skupiny prvního člena",
"added": "přidáno",
"admin": "administrátor",
@ -77,7 +76,6 @@
"create_new_folder": "Vytvořit novou složku",
"create_new_subscription": "Vytvořit nové předplatné.",
"create_project_in_github": "Vytvořit GitHub repozitář.",
"create_your_first_project": "Vytvořte svůj první projekt!",
"creating": "Vytvářím",
"cs": "Čeština",
"current_password": "Aktuální heslo",
@ -220,7 +218,6 @@
"online_latex_editor": "Online LaTeX editor",
"optional": "Dobrovolný",
"or": "nebo",
"or_create_project_left": "nebo nalevo vytvořit váš prví projekt.",
"other_logs_and_files": "Ostatní logy a soubory",
"over": "více než",
"over_x_templates_easy_getting_started": "V naší galerii naleznete tísíce __templates__, takže he opravdu jednoduché začít, ať už chcete psát článek do časopisu, závěrečnou práci, CV, nebo něco jiného.",

View file

@ -53,7 +53,6 @@
"add_or_remove_project_from_tag": "Føj projekt til, eller fjern projekt fra, tagget __tagName__",
"add_role_and_department": "Tilføj rolle og afdeling",
"add_to_folder": "Tilføj til mappe",
"add_to_folders": "Tilføj til mapper",
"add_your_comment_here": "Tilføj din kommentar her",
"add_your_first_group_member_now": "Tilføj de første medlemmer til din gruppe nu",
"added": "tilføjet",
@ -238,7 +237,6 @@
"create_new_folder": "Opret ny mappe",
"create_new_subscription": "Lav nyt abonnement",
"create_project_in_github": "Skab et GitHub lager",
"create_your_first_project": "Lav dit første projekt!",
"created_at": "Oprettet",
"creating": "Opretter",
"credit_card": "Kredit kort",
@ -703,7 +701,6 @@
"opted_out_linking": "Du har fravalgt at forbinde din <b>__appName__</b>konto for <b>__email__</b> til din institutionelle konto.",
"optional": "Valgfrit",
"or": "eller",
"or_create_project_left": "eller opret dit første projekt til venstre.",
"other_actions": "Andre handlinger",
"other_logs_and_files": "Andre logger og filer",
"other_output_files": "Hent andre udfiler",

View file

@ -49,7 +49,6 @@
"add_new_email": "Neue E-Mail-Adresse hinzufügen",
"add_role_and_department": "Rolle und Abteilung hinzufügen",
"add_to_folder": "Zu Ordner hinzufügen",
"add_to_folders": "Zu Ordnern hinzufügen",
"add_your_comment_here": "Füge hier einen Kommentar hinzu",
"add_your_first_group_member_now": "Füge jetzt dein erstes Gruppenmitglied hinzu",
"added": "hinzugefügt",
@ -251,7 +250,6 @@
"create_new_folder": "Neuen Ordner erstellen",
"create_new_subscription": "Neues Abonnement erstellen",
"create_project_in_github": "Ein GitHub Repository erstellen",
"create_your_first_project": "Erstelle dein erstes Projekt!",
"creating": "Erstellung läuft",
"credit_card": "Kreditkarte",
"cs": "Tschechisch",
@ -851,7 +849,6 @@
"opted_out_linking": "Du hast dich gegen die Verknüpfung deines <b>__email__</b> <b>__appName__</b>-Kontos mit deinem institutionellen Konto entschieden.",
"optional": "Freiwillig",
"or": "oder",
"or_create_project_left": "oder erstelle dein erstes Projekt auf der linken Seite.",
"organization": "Organisation",
"other_actions": "Weitere Aktionen",
"other_logs_and_files": "Andere Protokolle und Dateien",

View file

@ -60,8 +60,6 @@
"add_new_email": "Add new email",
"add_or_remove_project_from_tag": "Add or remove project from tag __tagName__",
"add_role_and_department": "Add role and department",
"add_to_folder": "Add to folder",
"add_to_folders": "Add to folders",
"add_to_tag": "Add to tag",
"add_your_comment_here": "Add your comment here",
"add_your_first_group_member_now": "Add your first group members now",
@ -301,8 +299,6 @@
"create_new_subscription": "Create New Subscription",
"create_new_tag": "Create new tag",
"create_project_in_github": "Create a GitHub repository",
"create_your_first_project": "Create your first project!",
"created": "created",
"created_at": "Created at",
"creating": "Creating",
"credit_card": "Credit Card",
@ -927,7 +923,6 @@
"main_file_not_found": "Unknown main document",
"maintenance": "Maintenance",
"make_a_copy": "Make a copy",
"make_copy": "Make a copy",
"make_email_primary_description": "Make this the primary email, used to log in",
"make_primary": "Make Primary",
"make_private": "Make Private",
@ -1079,7 +1074,6 @@
"opted_out_linking": "Youve opted out from linking your <b>__email__</b> <b>__appName__</b> account to your institutional account.",
"optional": "Optional",
"or": "or",
"or_create_project_left": "or create your first project on the left.",
"organization": "Organization",
"organization_type": "Organization Type",
"organize_projects": "Organize Projects",
@ -1530,7 +1524,6 @@
"tagline_student_annual": "Save even more",
"tagline_student_monthly": "Great for a single term",
"tags": "Tags",
"tags_slash_folders": "Tags/Folders",
"take_me_home": "Take me home!",
"take_short_survey": "Take a short survey",
"tc_everyone": "Everyone",

View file

@ -24,7 +24,6 @@
"add": "Agregar",
"add_more_members": "Agregar más miembros",
"add_to_folder": "Agrega a una carpeta",
"add_to_folders": "Agregar a carpetas",
"add_your_first_group_member_now": "Agrega tu primer grupo de miembros ahora",
"added": "agregado",
"adding": "Añadiendo",
@ -103,7 +102,6 @@
"create_new_folder": "Crear nueva carpeta",
"create_new_subscription": "Crear nueva suscripción",
"create_project_in_github": "Crear un repositorio en GitHub",
"create_your_first_project": "¡Crea tu primer proyecto!",
"creating": "Creando",
"credit_card": "Tarjeta de crédito",
"cs": "Checo",
@ -290,7 +288,6 @@
"open_a_file_on_the_left": "Abrir un archivo a la izquierda",
"optional": "Opcional",
"or": "o",
"or_create_project_left": "o crea tu primer proyecto a la izquierda.",
"other_logs_and_files": "Otros logs y archivos",
"over": "más",
"over_x_templates_easy_getting_started": "Hay __over__ de 400 __templates__ en nuestra galería de plantillas, así que es bastante fácil empezar, ya sea que estés escribiendo un artículo científico, una tesis, un CV u otro.",

View file

@ -10,7 +10,6 @@
"add": "Lisää",
"add_more_members": "Lisää jäseniä",
"add_to_folder": "Lisää kansioon",
"add_to_folders": "Lisää kansioihin",
"add_your_first_group_member_now": "Lisää ensimmäiset ryhmäsi jäsenet nyt",
"added": "lisätty",
"address": "Osoite",
@ -79,7 +78,6 @@
"create_new_folder": "Luo uusi kansio",
"create_new_subscription": "Luo uusi tilaus",
"create_project_in_github": "Luo GitHub-repository",
"create_your_first_project": "Luo ensimmäinen projektisi!",
"creating": "Luodaan",
"credit_card": "Luottokortti",
"cs": "Tsekki",
@ -229,7 +227,6 @@
"online_latex_editor": "Verkossa toimiva LaTeX-editori",
"optional": "Valinnainen",
"or": "tai",
"or_create_project_left": "tai luo ensimmäinen projekti vasemmalta.",
"other_logs_and_files": "Muut lokit &amp; tiedostot",
"over": "yli",
"over_x_templates_easy_getting_started": "Mallipohjagalleriastamme löytyy __yli__ 400 __mallipohjaa__, joten pääset todella helposti alkuun, kirjoitit sitten tiedeartikkelia, väitöstä, ansioluetteloa tai jotain muuta.",

View file

@ -47,7 +47,6 @@
"add_new_email": "Ajouter ladresse",
"add_role_and_department": "Ajouter votre rôle et votre département",
"add_to_folder": "Ajouter au dossier",
"add_to_folders": "Ajouter aux dossiers",
"add_your_comment_here": "Ajoutez votre commentaire ici",
"add_your_first_group_member_now": "Ajouter le premier membre de votre groupe maintenant",
"added": "ajouté",
@ -228,7 +227,6 @@
"create_new_folder": "Créer un nouveau dossier",
"create_new_subscription": "Créer un nouvel abonnement",
"create_project_in_github": "Créer un dépôt GitHub",
"create_your_first_project": "Créer votre premier projet !",
"creating": "Création en cours",
"credit_card": "Carte bleue",
"cs": "Tchéque",
@ -703,7 +701,6 @@
"opted_out_linking": "Vous avez choisi de ne pas lier votre compte <b>__appName__</b> <b>__email__</b> à votre compte institutionnel.",
"optional": "Optionnel",
"or": "ou",
"or_create_project_left": "ou créez votre premier projet à gauche.",
"other_actions": "Autres actions",
"other_logs_and_files": "Autres journaux et fichiers",
"other_output_files": "Télécharger les autres fichiers générés",

View file

@ -19,7 +19,6 @@
"add": "Aggiungi",
"add_more_members": "Aggiungi membri",
"add_to_folder": "Aggiungi a cartella",
"add_to_folders": "Aggiungi a cartelle",
"add_your_first_group_member_now": "Aggiungi ora i primi membri del gruppo",
"added": "aggiunto",
"adding": "Aggiunta",
@ -93,7 +92,6 @@
"create_new_folder": "Crea Nuova Cartella",
"create_new_subscription": "Crea Nuovo Abbonamento",
"create_project_in_github": "Crea un repository GitHub",
"create_your_first_project": "Crea il tuo primo progetto!",
"creating": "Creazione",
"credit_card": "Carta di Credito",
"cs": "Ceco",
@ -256,7 +254,6 @@
"online_latex_editor": "Editor LaTeX online",
"optional": "Opzionale",
"or": "o",
"or_create_project_left": "o crea il tuo primo progetto a sinistra.",
"other_logs_and_files": "Altri log &amp; file",
"over": "su",
"over_x_templates_easy_getting_started": "Esistono __over__ 400 __templates__ nella nostra galleria di modelli, ed è quindi molto facile iniziare, sia che tu stia scrivendo un articolo, una tesi, un CV o qualcos altro.",

View file

@ -25,7 +25,6 @@
"add": "追加",
"add_more_members": "メンバーの追加",
"add_to_folder": "フォルダに追加",
"add_to_folders": "フォルダに追加",
"add_your_first_group_member_now": "最初のグループメンバーを今すぐ追加",
"added": "追加",
"adding": "追加中",
@ -120,7 +119,6 @@
"create_new_folder": "新規フォルダの作成",
"create_new_subscription": "新しい購読の作成",
"create_project_in_github": "GitHubリポジトリの作成",
"create_your_first_project": "最初のプロジェクトを作成!",
"creating": "作成中",
"credit_card": "クレジットカード",
"cs": "チェコ語",
@ -335,7 +333,6 @@
"open_project": "プロジェクトを開く",
"optional": "オプショナル",
"or": "または",
"or_create_project_left": "あるいは、左で最初のプロジェクトを作成してください。",
"other_actions": "その他の操作",
"other_logs_and_files": "他のログとファイル",
"over": "以上",

View file

@ -29,7 +29,6 @@
"add_comment": "코멘트 추가",
"add_more_members": "더많은 멤버 추가",
"add_to_folder": "폴더에 추가하기",
"add_to_folders": "폴더 추가",
"add_your_comment_here": "여기에 코멘트 추가",
"add_your_first_group_member_now": "지금 첫 그룹 멤버 추가",
"added": "추가완료",
@ -142,7 +141,6 @@
"create_new_folder": "새로운 폴더 만들기",
"create_new_subscription": "새로운 구독 만들기",
"create_project_in_github": "GitHub 저장소 만들기",
"create_your_first_project": "첫 프로젝트를 만드세요!",
"creating": "만드는 중",
"credit_card": "신용카드",
"cs": "Čeština",
@ -380,7 +378,6 @@
"open_project": "프로젝트 열기",
"optional": "선택사항",
"or": "또는",
"or_create_project_left": "또는 왼쪽에 첫 프로젝트를 만드세요.",
"other_actions": "다른 방법들",
"other_logs_and_files": "기타 로그 및 파일 출력",
"over": "더 많은",

View file

@ -34,7 +34,6 @@
"add_new_email": "Voeg nieuwe email toe",
"add_role_and_department": "Voeg rol en afdeling toe",
"add_to_folder": "Toevoegen aan map",
"add_to_folders": "Aan mappen toevoegen",
"add_your_comment_here": "Voeg uw opmerking hier toe",
"add_your_first_group_member_now": "Voeg je eerste groepsleden nu toe",
"added": "toegevoegd",
@ -144,7 +143,6 @@
"create_new_folder": "Nieuwe Map Maken",
"create_new_subscription": "Nieuw Abonnement Maken",
"create_project_in_github": "Een GitHub repository maken",
"create_your_first_project": "Maak je eerste project!",
"creating": "Aan het maken",
"credit_card": "Creditcard",
"cs": "Tsjechisch",
@ -398,7 +396,6 @@
"open_project": "Open Project",
"optional": "Optioneel",
"or": "of",
"or_create_project_left": "of maak je eerste project aan de linkerkant.",
"other_actions": "Andere acties",
"other_logs_and_files": "Andere logs en bestanden",
"over": "meer dan",

View file

@ -21,7 +21,6 @@
"add": "Legg til",
"add_more_members": "Legg til flere medlemmer",
"add_to_folder": "Legg til i mappe",
"add_to_folders": "Legg til i mapper",
"add_your_first_group_member_now": "Legg til ditt første gruppemedlem nå",
"added": "lagt til",
"adding": "Legge til",
@ -98,7 +97,6 @@
"create_new_folder": "Lag ny mappe",
"create_new_subscription": "Lag nytt abonnement",
"create_project_in_github": "Lag et GitHub-repository",
"create_your_first_project": "Opprett ditt første prosjekt",
"creating": "Oppretter",
"credit_card": "Kredittkort",
"cs": "Tsjekkisk",
@ -264,7 +262,6 @@
"online_latex_editor": "Online LaTeX-redigeringsprogram",
"optional": "Valgfri",
"or": "eller",
"or_create_project_left": "eller opprett ditt første prosjekt til venstre.",
"other_logs_and_files": "Andre logger &amp; filer",
"over": "over",
"over_x_templates_easy_getting_started": "Vi har __over__ 400 __templates__ i malgalleriet vårt, så det er enkelt å komme i gang, enten du skriver en avhandling, et tidsskrift, CV eller noe annet.",

View file

@ -47,7 +47,6 @@
"copying": "kopiowanie",
"create": "Utwórz",
"create_new_folder": "Utwórz nowy folder",
"create_your_first_project": "Utwórz swój pierwszy projekt!",
"creating": "Tworzenie",
"cs": "Czeski",
"current_password": "Aktualne hasło",
@ -136,7 +135,6 @@
"one_collaborator": "Tylko jeden współpracownik",
"one_free_collab": "Jeden darmowy współpracownik",
"or": "lub",
"or_create_project_left": "lub utwórz swój pierwszy projekt (z lewej).",
"other_logs_and_files": "Inne logi i pliki",
"owner": "Właściciel",
"page_not_found": "Strona nie znaleziona",

View file

@ -37,7 +37,6 @@
"add_new_email": "Adicionar novo e-mail",
"add_role_and_department": "Adicionar perfil e departamento",
"add_to_folder": "Adicionar à pasta",
"add_to_folders": "Adicionar à pasta",
"add_your_comment_here": "Adicione seu comentário aqui",
"add_your_first_group_member_now": "Adicione seu primeiro membro no grupo agora",
"added": "adicionado",
@ -173,7 +172,6 @@
"create_new_folder": "Criar Nova Pasta",
"create_new_subscription": "Crie Nova Inscrição",
"create_project_in_github": "Criar um repositório no GitHub",
"create_your_first_project": "Crie seu primeiro projeto",
"creating": "Criando",
"credit_card": "Cartão de Crédito",
"cs": "Tcheco",
@ -484,7 +482,6 @@
"open_project": "Abrir Projeto",
"optional": "Opcional",
"or": "ou",
"or_create_project_left": "ou crie seu primeiro projeto na esquerda",
"other_actions": "Outras Ações",
"other_logs_and_files": "Outros Logs &amp; Arquivos",
"over": "mais de",

View file

@ -25,7 +25,6 @@
"add": "Добавить",
"add_more_members": "Добавить участников",
"add_to_folder": "Переместить в папку",
"add_to_folders": "Добавить в папки",
"add_your_first_group_member_now": "Добавьте первых участников группы сейчас",
"added": "добавлены",
"adding": "Добавление",
@ -110,7 +109,6 @@
"create_new_folder": "Создать папку",
"create_new_subscription": "Создать новую подписку",
"create_project_in_github": "Создать проект на GitHub",
"create_your_first_project": "Создайте свой первый проект!",
"creating": "Создание",
"credit_card": "банковская карта",
"cs": "Чешский",
@ -304,7 +302,6 @@
"open_project": "Открыть проект",
"optional": "Необязательный",
"or": "или",
"or_create_project_left": "или создайте свой первый проект слева.",
"other_logs_and_files": "Другие логи и файлы",
"over": "свыше",
"over_x_templates_easy_getting_started": "В нашей галереи содержится __более__ 400 __шаблонов__, так что Вы можете легко начать работу над Вашей статьёй для журнала, диссертацией, резюме или любым другим документом.",

View file

@ -46,7 +46,6 @@
"add_new_email": "Lägg till ny e-postadress",
"add_role_and_department": "Lägg till befattning och avdelning",
"add_to_folder": "Lägg till i mapp",
"add_to_folders": "Lägg till i mappar",
"add_your_comment_here": "Skriv din kommentar här",
"add_your_first_group_member_now": "Lägg till dina första gruppmedlemmar nu",
"added": "lagst till",
@ -223,7 +222,6 @@
"create_new_folder": "Skapa ny mapp",
"create_new_subscription": "Skapa en ny prenumeration",
"create_project_in_github": "Skapa ett GitHub repo",
"create_your_first_project": "Skapa ditt första projekt!",
"creating": "Skapar",
"credit_card": "Kreditkort",
"cs": "Tjeckiska",
@ -676,7 +674,6 @@
"open_project": "Öppna projekt",
"optional": "Valfritt",
"or": "eller",
"or_create_project_left": "eller skapa ditt första projekt till vänster.",
"other_actions": "Andra åtgärder",
"other_logs_and_files": "Andra loggar och filer",
"other_output_files": "Ladda ner andra utdatafiler",

View file

@ -23,7 +23,6 @@
"add": "Ekle",
"add_more_members": "Daha fazla üye ekleyin",
"add_to_folder": "Klasöre ekle",
"add_to_folders": "Klasörlere ekle",
"add_your_first_group_member_now": "Grubunuza ilk üyeleri ekleyin",
"added": "eklenmiş",
"adding": "Ekleniyor",
@ -96,7 +95,6 @@
"create_new_folder": "Yeni Klasör Oluştur",
"create_new_subscription": "Yeni Abonelik Oluştur",
"create_project_in_github": "GitHub deposu oluştur",
"create_your_first_project": "İlk projenizi oluşturun!",
"creating": "Oluşturuluyor",
"credit_card": "Kredi Kartı",
"cs": "Çekçe",
@ -256,7 +254,6 @@
"online_latex_editor": "Çevrimiçi LaTeX Editörü",
"optional": "İsteğe bağlı",
"or": " ya da",
"or_create_project_left": "ya da ilk projenizi sol taraftan oluşturabilirsiniz.",
"other_logs_and_files": "Diğer sonuç dökümleri &amp; dosyalar",
"over": "fazla",
"over_x_templates_easy_getting_started": "Şablon galerimizde sayısı 400den __over__ olan __templates__ işe başlamanızı kolaylaştırabilir. Şablonlar arasında dergi makalesi, tez, CV ve daha fazlası bulunmaktadır.",

View file

@ -47,7 +47,6 @@
"add_new_email": "添加新电子邮件",
"add_role_and_department": "添加角色和部门",
"add_to_folder": "添加到目录",
"add_to_folders": "添加到目录",
"add_your_comment_here": "在此添加评论",
"add_your_first_group_member_now": "现在添加您的第一个组成员",
"added": "已添加",
@ -228,7 +227,6 @@
"create_new_folder": "创建新目录",
"create_new_subscription": "新建订购",
"create_project_in_github": "创建一个GitHub存储库",
"create_your_first_project": "创建您的第一个项目!",
"creating": "正在创建",
"credit_card": "信用卡",
"cs": "捷克语",
@ -699,7 +697,6 @@
"opted_out_linking": "您已选择取消将您的 <b>__email__</b> <b>__appName__</b> 帐户绑定到您的机构帐户。",
"optional": "可选的",
"or": "或者",
"or_create_project_left": "或者在左边创建您的第一个项目",
"other_actions": "其他",
"other_logs_and_files": "其他日志和文件",
"other_output_files": "下载其他输出文件",