mirror of
https://github.com/overleaf/overleaf.git
synced 2024-09-16 02:52:31 -04:00
Merge pull request #2135 from overleaf/sk-hide-info-readonly-token-access
Restrict information and features for token-read-only users of a project GitOrigin-RevId: 98512fb9a916f430fd635fd5634c37799476010d
This commit is contained in:
parent
7c533fa5fb
commit
deaf76be39
8 changed files with 332 additions and 209 deletions
|
@ -708,6 +708,8 @@ module.exports = ProjectController = {
|
||||||
anonymous,
|
anonymous,
|
||||||
anonymousAccessToken: req._anonymousAccessToken,
|
anonymousAccessToken: req._anonymousAccessToken,
|
||||||
isTokenMember,
|
isTokenMember,
|
||||||
|
isRestrictedTokenMember:
|
||||||
|
isTokenMember === true && privilegeLevel === 'readOnly',
|
||||||
languages: Settings.languages,
|
languages: Settings.languages,
|
||||||
editorThemes: THEME_LIST,
|
editorThemes: THEME_LIST,
|
||||||
maxDocLength: Settings.max_doc_length,
|
maxDocLength: Settings.max_doc_length,
|
||||||
|
@ -828,6 +830,10 @@ module.exports = ProjectController = {
|
||||||
tokens: project.tokens,
|
tokens: project.tokens,
|
||||||
isV1Project: false
|
isV1Project: false
|
||||||
}
|
}
|
||||||
|
if (accessLevel === PrivilegeLevels.READ_ONLY && source === Sources.TOKEN) {
|
||||||
|
model.owner_ref = null
|
||||||
|
model.lastUpdatedBy = null
|
||||||
|
}
|
||||||
return model
|
return model
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,8 @@ block content
|
||||||
include ./editor/history
|
include ./editor/history
|
||||||
|
|
||||||
.ui-layout-east
|
.ui-layout-east
|
||||||
include ./editor/chat
|
if !isRestrictedTokenMember
|
||||||
|
include ./editor/chat
|
||||||
|
|
||||||
include ./editor/hotkeys
|
include ./editor/hotkeys
|
||||||
|
|
||||||
|
@ -140,6 +141,7 @@ block requirejs
|
||||||
window.brandVariation = data.brandVariation;
|
window.brandVariation = data.brandVariation;
|
||||||
window.anonymousAccessToken = "#{anonymousAccessToken}";
|
window.anonymousAccessToken = "#{anonymousAccessToken}";
|
||||||
window.isTokenMember = #{!!isTokenMember};
|
window.isTokenMember = #{!!isTokenMember};
|
||||||
|
window.isRestrictedTokenMember = #{!!isRestrictedTokenMember};
|
||||||
window.maxDocLength = #{maxDocLength};
|
window.maxDocLength = #{maxDocLength};
|
||||||
window.trackChangesState = data.trackChangesState;
|
window.trackChangesState = data.trackChangesState;
|
||||||
window.wikiEnabled = #{!!(settings.apis.wiki && settings.apis.wiki.url)};
|
window.wikiEnabled = #{!!(settings.apis.wiki && settings.apis.wiki.url)};
|
||||||
|
|
|
@ -83,7 +83,7 @@ header.toolbar.toolbar-header.toolbar-with-labels(
|
||||||
popover-trigger="mouseenter"
|
popover-trigger="mouseenter"
|
||||||
ng-click="gotoUser(user)"
|
ng-click="gotoUser(user)"
|
||||||
) {{ userInitial(user) }}
|
) {{ userInitial(user) }}
|
||||||
|
|
||||||
span.dropdown(dropdown, ng-if="onlineUsersArray.length >= 4")
|
span.dropdown(dropdown, ng-if="onlineUsersArray.length >= 4")
|
||||||
span.online-user.online-user-multi(
|
span.online-user.online-user-multi(
|
||||||
dropdown-toggle,
|
dropdown-toggle,
|
||||||
|
@ -101,16 +101,17 @@ header.toolbar.toolbar-header.toolbar-with-labels(
|
||||||
) {{ user.name.slice(0,1) }}
|
) {{ user.name.slice(0,1) }}
|
||||||
| {{ user.name }}
|
| {{ user.name }}
|
||||||
|
|
||||||
a.btn.btn-full-height(
|
if !isRestrictedTokenMember
|
||||||
href,
|
a.btn.btn-full-height(
|
||||||
ng-if="project.features.trackChangesVisible",
|
href,
|
||||||
ng-class="{ active: ui.reviewPanelOpen && ui.view !== 'history' }"
|
ng-if="project.features.trackChangesVisible",
|
||||||
ng-disabled="ui.view === 'history'"
|
ng-class="{ active: ui.reviewPanelOpen && ui.view !== 'history' }"
|
||||||
ng-click="toggleReviewPanel()"
|
ng-disabled="ui.view === 'history'"
|
||||||
)
|
ng-click="toggleReviewPanel()"
|
||||||
i.review-icon
|
)
|
||||||
p.toolbar-label
|
i.review-icon
|
||||||
| #{translate("review")}
|
p.toolbar-label
|
||||||
|
| #{translate("review")}
|
||||||
|
|
||||||
a.btn.btn-full-height(
|
a.btn.btn-full-height(
|
||||||
href
|
href
|
||||||
|
@ -122,24 +123,25 @@ header.toolbar.toolbar-header.toolbar-with-labels(
|
||||||
|
|
||||||
!= moduleIncludes('publish:button', locals)
|
!= moduleIncludes('publish:button', locals)
|
||||||
|
|
||||||
a.btn.btn-full-height(
|
if !isRestrictedTokenMember
|
||||||
href,
|
a.btn.btn-full-height(
|
||||||
ng-click="toggleHistory();",
|
href,
|
||||||
ng-class="{ active: (ui.view == 'history') }",
|
ng-click="toggleHistory();",
|
||||||
)
|
ng-class="{ active: (ui.view == 'history') }",
|
||||||
i.fa.fa-fw.fa-history
|
|
||||||
p.toolbar-label #{translate("history")}
|
|
||||||
a.btn.btn-full-height(
|
|
||||||
href,
|
|
||||||
ng-class="{ active: ui.chatOpen }",
|
|
||||||
ng-click="toggleChat();",
|
|
||||||
ng-controller="ChatButtonController",
|
|
||||||
ng-show="!anonymous",
|
|
||||||
)
|
|
||||||
i.fa.fa-fw.fa-comment(
|
|
||||||
ng-class="{ 'bounce': unreadMessages > 0 }"
|
|
||||||
)
|
)
|
||||||
span.label.label-info(
|
i.fa.fa-fw.fa-history
|
||||||
ng-show="unreadMessages > 0"
|
p.toolbar-label #{translate("history")}
|
||||||
) {{ unreadMessages }}
|
a.btn.btn-full-height(
|
||||||
p.toolbar-label #{translate("chat")}
|
href,
|
||||||
|
ng-class="{ active: ui.chatOpen }",
|
||||||
|
ng-click="toggleChat();",
|
||||||
|
ng-controller="ChatButtonController",
|
||||||
|
ng-show="!anonymous",
|
||||||
|
)
|
||||||
|
i.fa.fa-fw.fa-comment(
|
||||||
|
ng-class="{ 'bounce': unreadMessages > 0 }"
|
||||||
|
)
|
||||||
|
span.label.label-info(
|
||||||
|
ng-show="unreadMessages > 0"
|
||||||
|
) {{ unreadMessages }}
|
||||||
|
p.toolbar-label #{translate("chat")}
|
||||||
|
|
|
@ -8,181 +8,191 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
|
||||||
h3 #{translate("share_project")}
|
h3 #{translate("share_project")}
|
||||||
.modal-body.modal-body-share
|
.modal-body.modal-body-share
|
||||||
.container-fluid
|
.container-fluid
|
||||||
//- Private (with token-access available)
|
|
||||||
.row.public-access-level(ng-show="isAdmin && project.publicAccesLevel == 'private'")
|
|
||||||
.col-xs-12.text-center
|
|
||||||
| #{translate('link_sharing_is_off')}
|
|
||||||
|
|
|
||||||
a(
|
|
||||||
href
|
|
||||||
ng-click="makeTokenBased()"
|
|
||||||
) #{translate('turn_on_link_sharing')}
|
|
||||||
span
|
|
||||||
a(
|
|
||||||
href="/learn/how-to/What_is_Link_Sharing%3F"
|
|
||||||
target="_blank"
|
|
||||||
)
|
|
||||||
i.fa.fa-question-circle(
|
|
||||||
tooltip=translate('learn_more_about_link_sharing')
|
|
||||||
)
|
|
||||||
|
|
||||||
//- Token-based access
|
if isRestrictedTokenMember
|
||||||
.row.public-access-level(ng-show="isAdmin && project.publicAccesLevel == 'tokenBased'")
|
//- Token-based access
|
||||||
.col-xs-12.text-center
|
.row.public-access-level
|
||||||
strong
|
.col-xs-12.access-token-display-area
|
||||||
| #{translate('link_sharing_is_on')}.
|
div.access-token-wrapper
|
||||||
|
strong #{translate('anyone_with_link_can_view')}
|
||||||
|
pre.access-token {{ readOnlyTokenLink }}
|
||||||
|
|
||||||
|
if !isRestrictedTokenMember
|
||||||
|
//- Private (with token-access available)
|
||||||
|
.row.public-access-level(ng-show="isAdmin && project.publicAccesLevel == 'private'")
|
||||||
|
.col-xs-12.text-center
|
||||||
|
| #{translate('link_sharing_is_off')}
|
||||||
|
|
|
|
||||||
a(
|
a(
|
||||||
href
|
|
||||||
ng-click="makePrivate()"
|
|
||||||
) #{translate('turn_off_link_sharing')}
|
|
||||||
span
|
|
||||||
a(
|
|
||||||
href="/learn/how-to/What_is_Link_Sharing%3F"
|
|
||||||
target="_blank"
|
|
||||||
)
|
|
||||||
i.fa.fa-question-circle(
|
|
||||||
tooltip=translate('learn_more_about_link_sharing')
|
|
||||||
)
|
|
||||||
|
|
||||||
.col-xs-12.access-token-display-area
|
|
||||||
div.access-token-wrapper
|
|
||||||
strong #{translate('anyone_with_link_can_edit')}
|
|
||||||
pre.access-token(ng-show="readAndWriteTokenLink") {{ readAndWriteTokenLink }}
|
|
||||||
pre.access-token(ng-hide="readAndWriteTokenLink") #{translate('loading')}...
|
|
||||||
div.access-token-wrapper
|
|
||||||
strong #{translate('anyone_with_link_can_view')}
|
|
||||||
pre.access-token(ng-show="readOnlyTokenLink") {{ readOnlyTokenLink }}
|
|
||||||
pre.access-token(ng-hide="readOnlyTokenLink") #{translate('loading')}...
|
|
||||||
|
|
||||||
//- legacy public-access
|
|
||||||
.row.public-access-level(ng-show="isAdmin && (project.publicAccesLevel == 'readAndWrite' || project.publicAccesLevel == 'readOnly')")
|
|
||||||
.col-xs-12.text-center
|
|
||||||
strong(ng-if="project.publicAccesLevel == 'readAndWrite'") #{translate("this_project_is_public")}
|
|
||||||
strong(ng-if="project.publicAccesLevel == 'readOnly'") #{translate("this_project_is_public_read_only")}
|
|
||||||
|
|
|
||||||
a(
|
|
||||||
href
|
|
||||||
ng-click="makePrivate()"
|
|
||||||
) #{translate("make_private")}
|
|
||||||
|
|
||||||
.row.project-member
|
|
||||||
.col-xs-8 {{ project.owner.email }}
|
|
||||||
.text-left(
|
|
||||||
ng-class="{'col-xs-3': project.members.length > 0, 'col-xs-4': project.members.length == 0}"
|
|
||||||
) #{translate("owner")}
|
|
||||||
.row.project-member(ng-repeat="member in project.members")
|
|
||||||
.col-xs-8 {{ member.email }}
|
|
||||||
.col-xs-3.text-left
|
|
||||||
span(ng-show="member.privileges == 'readAndWrite'") #{translate("can_edit")}
|
|
||||||
span(ng-show="member.privileges == 'readOnly'") #{translate("read_only")}
|
|
||||||
.col-xs-1(ng-show="isAdmin")
|
|
||||||
a(
|
|
||||||
href
|
|
||||||
tooltip=translate('remove_collaborator')
|
|
||||||
tooltip-placement="bottom"
|
|
||||||
ng-click="removeMember(member)"
|
|
||||||
)
|
|
||||||
i.fa.fa-times
|
|
||||||
.row.project-invite(ng-repeat="invite in project.invites")
|
|
||||||
.col-xs-8 {{ invite.email }}
|
|
||||||
div.small
|
|
||||||
| #{translate("invite_not_accepted")}.
|
|
||||||
button.btn.btn-inline-link(
|
|
||||||
ng-show="isAdmin",
|
|
||||||
ng-click="resendInvite(invite, $event)"
|
|
||||||
) #{translate("resend")}
|
|
||||||
.col-xs-3.text-left
|
|
||||||
// todo: get invite privileges
|
|
||||||
span(ng-show="invite.privileges == 'readAndWrite'") #{translate("can_edit")}
|
|
||||||
span(ng-show="invite.privileges == 'readOnly'") #{translate("read_only")}
|
|
||||||
.col-xs-1(ng-show="isAdmin")
|
|
||||||
a(
|
|
||||||
href
|
|
||||||
tooltip=translate('revoke_invite')
|
|
||||||
tooltip-placement="bottom"
|
|
||||||
ng-click="revokeInvite(invite)"
|
|
||||||
)
|
|
||||||
i.fa.fa-times
|
|
||||||
.row.invite-controls(ng-show="isAdmin")
|
|
||||||
form(ng-show="canAddCollaborators")
|
|
||||||
.small #{translate("share_with_your_collabs")}
|
|
||||||
.form-group
|
|
||||||
tags-input(
|
|
||||||
template="shareTagTemplate"
|
|
||||||
placeholder=settings.customisation.shareProjectPlaceholder || 'joe@example.com, sue@example.com, ...'
|
|
||||||
ng-model="inputs.contacts"
|
|
||||||
focus-on="open"
|
|
||||||
display-property="display"
|
|
||||||
add-on-paste="true"
|
|
||||||
add-on-enter="false"
|
|
||||||
replace-spaces-with-dashes="false"
|
|
||||||
type="email"
|
|
||||||
)
|
|
||||||
auto-complete(
|
|
||||||
source="filterAutocompleteUsers($query)"
|
|
||||||
template="shareAutocompleteTemplate"
|
|
||||||
display-property="email"
|
|
||||||
min-length="0"
|
|
||||||
)
|
|
||||||
.form-group
|
|
||||||
.pull-right
|
|
||||||
select.privileges.form-control(
|
|
||||||
ng-model="inputs.privileges"
|
|
||||||
name="privileges"
|
|
||||||
)
|
|
||||||
option(value="readAndWrite") #{translate("can_edit")}
|
|
||||||
option(value="readOnly") #{translate("read_only")}
|
|
||||||
|
|
|
||||||
//- We have to use mousedown here since click has issues with the
|
|
||||||
//- blur handler in tags-input sometimes changing its height and
|
|
||||||
//- moving this button, preventing the click registering.
|
|
||||||
button.btn.btn-info(
|
|
||||||
type="submit"
|
|
||||||
ng-mousedown="addMembers()"
|
|
||||||
ng-keyup="$event.keyCode == 13 ? addMembers() : null"
|
|
||||||
) #{translate("share")}
|
|
||||||
div(ng-hide="canAddCollaborators")
|
|
||||||
p.text-center #{translate("need_to_upgrade_for_more_collabs")}. Also:
|
|
||||||
.row
|
|
||||||
.col-md-8.col-md-offset-2
|
|
||||||
ul.list-unstyled
|
|
||||||
li
|
|
||||||
i.fa.fa-check
|
|
||||||
| #{translate("unlimited_projects")}
|
|
||||||
|
|
||||||
li
|
|
||||||
i.fa.fa-check
|
|
||||||
| #{translate("collabs_per_proj", {collabcount:'Multiple'})}
|
|
||||||
|
|
||||||
li
|
|
||||||
i.fa.fa-check
|
|
||||||
| #{translate("full_doc_history")}
|
|
||||||
|
|
||||||
li
|
|
||||||
i.fa.fa-check
|
|
||||||
| #{translate("sync_to_dropbox")}
|
|
||||||
|
|
||||||
li
|
|
||||||
i.fa.fa-check
|
|
||||||
| #{translate("sync_to_github")}
|
|
||||||
|
|
||||||
li
|
|
||||||
i.fa.fa-check
|
|
||||||
|#{translate("compile_larger_projects")}
|
|
||||||
|
|
||||||
p.text-center.row-spaced-thin(ng-controller="FreeTrialModalController")
|
|
||||||
a.btn.btn-success(
|
|
||||||
href
|
href
|
||||||
ng-class="buttonClass"
|
ng-click="makeTokenBased()"
|
||||||
ng-click="startFreeTrial('projectMembers')"
|
) #{translate('turn_on_link_sharing')}
|
||||||
) #{translate("start_free_trial")}
|
span
|
||||||
|
a(
|
||||||
|
href="/learn/how-to/What_is_Link_Sharing%3F"
|
||||||
|
target="_blank"
|
||||||
|
)
|
||||||
|
i.fa.fa-question-circle(
|
||||||
|
tooltip=translate('learn_more_about_link_sharing')
|
||||||
|
)
|
||||||
|
|
||||||
p.small(ng-show="startedFreeTrial")
|
//- Token-based access
|
||||||
| #{translate("refresh_page_after_starting_free_trial")}
|
.row.public-access-level(ng-show="isAdmin && project.publicAccesLevel == 'tokenBased'")
|
||||||
.row.public-access-level.public-access-level--notice(ng-show="!isAdmin")
|
.col-xs-12.text-center
|
||||||
.col-xs-12.text-center(ng-show="project.publicAccesLevel == 'private'") #{translate("to_add_more_collaborators")}
|
strong
|
||||||
.col-xs-12.text-center(ng-show="project.publicAccesLevel == 'tokenBased'") #{translate("to_change_access_permissions")}
|
| #{translate('link_sharing_is_on')}.
|
||||||
|
|
|
||||||
|
a(
|
||||||
|
href
|
||||||
|
ng-click="makePrivate()"
|
||||||
|
) #{translate('turn_off_link_sharing')}
|
||||||
|
span
|
||||||
|
a(
|
||||||
|
href="/learn/how-to/What_is_Link_Sharing%3F"
|
||||||
|
target="_blank"
|
||||||
|
)
|
||||||
|
i.fa.fa-question-circle(
|
||||||
|
tooltip=translate('learn_more_about_link_sharing')
|
||||||
|
)
|
||||||
|
|
||||||
|
.col-xs-12.access-token-display-area
|
||||||
|
div.access-token-wrapper
|
||||||
|
strong #{translate('anyone_with_link_can_edit')}
|
||||||
|
pre.access-token(ng-show="readAndWriteTokenLink") {{ readAndWriteTokenLink }}
|
||||||
|
pre.access-token(ng-hide="readAndWriteTokenLink") #{translate('loading')}...
|
||||||
|
div.access-token-wrapper
|
||||||
|
strong #{translate('anyone_with_link_can_view')}
|
||||||
|
pre.access-token(ng-show="readOnlyTokenLink") {{ readOnlyTokenLink }}
|
||||||
|
pre.access-token(ng-hide="readOnlyTokenLink") #{translate('loading')}...
|
||||||
|
|
||||||
|
//- legacy public-access
|
||||||
|
.row.public-access-level(ng-show="isAdmin && (project.publicAccesLevel == 'readAndWrite' || project.publicAccesLevel == 'readOnly')")
|
||||||
|
.col-xs-12.text-center
|
||||||
|
strong(ng-if="project.publicAccesLevel == 'readAndWrite'") #{translate("this_project_is_public")}
|
||||||
|
strong(ng-if="project.publicAccesLevel == 'readOnly'") #{translate("this_project_is_public_read_only")}
|
||||||
|
|
|
||||||
|
a(
|
||||||
|
href
|
||||||
|
ng-click="makePrivate()"
|
||||||
|
) #{translate("make_private")}
|
||||||
|
|
||||||
|
.row.project-member
|
||||||
|
.col-xs-8 {{ project.owner.email }}
|
||||||
|
.text-left(
|
||||||
|
ng-class="{'col-xs-3': project.members.length > 0, 'col-xs-4': project.members.length == 0}"
|
||||||
|
) #{translate("owner")}
|
||||||
|
.row.project-member(ng-repeat="member in project.members")
|
||||||
|
.col-xs-8 {{ member.email }}
|
||||||
|
.col-xs-3.text-left
|
||||||
|
span(ng-show="member.privileges == 'readAndWrite'") #{translate("can_edit")}
|
||||||
|
span(ng-show="member.privileges == 'readOnly'") #{translate("read_only")}
|
||||||
|
.col-xs-1(ng-show="isAdmin")
|
||||||
|
a(
|
||||||
|
href
|
||||||
|
tooltip=translate('remove_collaborator')
|
||||||
|
tooltip-placement="bottom"
|
||||||
|
ng-click="removeMember(member)"
|
||||||
|
)
|
||||||
|
i.fa.fa-times
|
||||||
|
.row.project-invite(ng-repeat="invite in project.invites")
|
||||||
|
.col-xs-8 {{ invite.email }}
|
||||||
|
div.small
|
||||||
|
| #{translate("invite_not_accepted")}.
|
||||||
|
button.btn.btn-inline-link(
|
||||||
|
ng-show="isAdmin",
|
||||||
|
ng-click="resendInvite(invite, $event)"
|
||||||
|
) #{translate("resend")}
|
||||||
|
.col-xs-3.text-left
|
||||||
|
// todo: get invite privileges
|
||||||
|
span(ng-show="invite.privileges == 'readAndWrite'") #{translate("can_edit")}
|
||||||
|
span(ng-show="invite.privileges == 'readOnly'") #{translate("read_only")}
|
||||||
|
.col-xs-1(ng-show="isAdmin")
|
||||||
|
a(
|
||||||
|
href
|
||||||
|
tooltip=translate('revoke_invite')
|
||||||
|
tooltip-placement="bottom"
|
||||||
|
ng-click="revokeInvite(invite)"
|
||||||
|
)
|
||||||
|
i.fa.fa-times
|
||||||
|
.row.invite-controls(ng-show="isAdmin")
|
||||||
|
form(ng-show="canAddCollaborators")
|
||||||
|
.small #{translate("share_with_your_collabs")}
|
||||||
|
.form-group
|
||||||
|
tags-input(
|
||||||
|
template="shareTagTemplate"
|
||||||
|
placeholder=settings.customisation.shareProjectPlaceholder || 'joe@example.com, sue@example.com, ...'
|
||||||
|
ng-model="inputs.contacts"
|
||||||
|
focus-on="open"
|
||||||
|
display-property="display"
|
||||||
|
add-on-paste="true"
|
||||||
|
add-on-enter="false"
|
||||||
|
replace-spaces-with-dashes="false"
|
||||||
|
type="email"
|
||||||
|
)
|
||||||
|
auto-complete(
|
||||||
|
source="filterAutocompleteUsers($query)"
|
||||||
|
template="shareAutocompleteTemplate"
|
||||||
|
display-property="email"
|
||||||
|
min-length="0"
|
||||||
|
)
|
||||||
|
.form-group
|
||||||
|
.pull-right
|
||||||
|
select.privileges.form-control(
|
||||||
|
ng-model="inputs.privileges"
|
||||||
|
name="privileges"
|
||||||
|
)
|
||||||
|
option(value="readAndWrite") #{translate("can_edit")}
|
||||||
|
option(value="readOnly") #{translate("read_only")}
|
||||||
|
|
|
||||||
|
//- We have to use mousedown here since click has issues with the
|
||||||
|
//- blur handler in tags-input sometimes changing its height and
|
||||||
|
//- moving this button, preventing the click registering.
|
||||||
|
button.btn.btn-info(
|
||||||
|
type="submit"
|
||||||
|
ng-mousedown="addMembers()"
|
||||||
|
ng-keyup="$event.keyCode == 13 ? addMembers() : null"
|
||||||
|
) #{translate("share")}
|
||||||
|
div(ng-hide="canAddCollaborators")
|
||||||
|
p.text-center #{translate("need_to_upgrade_for_more_collabs")}. Also:
|
||||||
|
.row
|
||||||
|
.col-md-8.col-md-offset-2
|
||||||
|
ul.list-unstyled
|
||||||
|
li
|
||||||
|
i.fa.fa-check
|
||||||
|
| #{translate("unlimited_projects")}
|
||||||
|
|
||||||
|
li
|
||||||
|
i.fa.fa-check
|
||||||
|
| #{translate("collabs_per_proj", {collabcount:'Multiple'})}
|
||||||
|
|
||||||
|
li
|
||||||
|
i.fa.fa-check
|
||||||
|
| #{translate("full_doc_history")}
|
||||||
|
|
||||||
|
li
|
||||||
|
i.fa.fa-check
|
||||||
|
| #{translate("sync_to_dropbox")}
|
||||||
|
|
||||||
|
li
|
||||||
|
i.fa.fa-check
|
||||||
|
| #{translate("sync_to_github")}
|
||||||
|
|
||||||
|
li
|
||||||
|
i.fa.fa-check
|
||||||
|
|#{translate("compile_larger_projects")}
|
||||||
|
|
||||||
|
p.text-center.row-spaced-thin(ng-controller="FreeTrialModalController")
|
||||||
|
a.btn.btn-success(
|
||||||
|
href
|
||||||
|
ng-class="buttonClass"
|
||||||
|
ng-click="startFreeTrial('projectMembers')"
|
||||||
|
) #{translate("start_free_trial")}
|
||||||
|
|
||||||
|
p.small(ng-show="startedFreeTrial")
|
||||||
|
| #{translate("refresh_page_after_starting_free_trial")}
|
||||||
|
.row.public-access-level.public-access-level--notice(ng-show="!isAdmin")
|
||||||
|
.col-xs-12.text-center(ng-show="project.publicAccesLevel == 'private'") #{translate("to_add_more_collaborators")}
|
||||||
|
.col-xs-12.text-center(ng-show="project.publicAccesLevel == 'tokenBased'") #{translate("to_change_access_permissions")}
|
||||||
.modal-footer.modal-footer-share
|
.modal-footer.modal-footer-share
|
||||||
.modal-footer-left
|
.modal-footer-left
|
||||||
i.fa.fa-refresh.fa-spin(ng-show="state.inflight")
|
i.fa.fa-refresh.fa-spin(ng-show="state.inflight")
|
||||||
|
@ -207,9 +217,9 @@ script(type="text/ng-template", id="shareTagTemplate")
|
||||||
.tag-template
|
.tag-template
|
||||||
span(ng-if="data.type")
|
span(ng-if="data.type")
|
||||||
i.fa.fa-fw(ng-class="{'fa-user': data.type != 'group', 'fa-group': data.type == 'group'}")
|
i.fa.fa-fw(ng-class="{'fa-user': data.type != 'group', 'fa-group': data.type == 'group'}")
|
||||||
|
|
|
|
||||||
span {{$getDisplayText()}}
|
span {{$getDisplayText()}}
|
||||||
|
|
|
|
||||||
a(href, ng-click="$removeTag()").remove-button
|
a(href, ng-click="$removeTag()").remove-button
|
||||||
i.fa.fa-fw.fa-close
|
i.fa.fa-fw.fa-close
|
||||||
|
|
||||||
|
@ -217,10 +227,10 @@ script(type="text/ng-template", id="shareAutocompleteTemplate")
|
||||||
.autocomplete-template
|
.autocomplete-template
|
||||||
div(ng-if="data.type == 'user'")
|
div(ng-if="data.type == 'user'")
|
||||||
i.fa.fa-fw.fa-user
|
i.fa.fa-fw.fa-user
|
||||||
|
|
|
|
||||||
span(ng-bind-html="$highlight(data.display)")
|
span(ng-bind-html="$highlight(data.display)")
|
||||||
div(ng-if="data.type == 'group'")
|
div(ng-if="data.type == 'group'")
|
||||||
i.fa.fa-fw.fa-group
|
i.fa.fa-fw.fa-group
|
||||||
|
|
|
|
||||||
span(ng-bind-html="$highlight(data.name)")
|
span(ng-bind-html="$highlight(data.name)")
|
||||||
span.subdued.small(ng-show="data.member_count") ({{ data.member_count }} members)
|
span.subdued.small(ng-show="data.member_count") ({{ data.member_count }} members)
|
||||||
|
|
|
@ -35,7 +35,7 @@ td.project-list-table-name-cell(ng-if-start="!project.isV1Project")
|
||||||
) ×
|
) ×
|
||||||
|
|
||||||
td.project-list-table-owner-cell
|
td.project-list-table-owner-cell
|
||||||
span.owner {{getOwnerName(project)}}
|
span.owner(ng-if='project.owner') {{getOwnerName(project)}}
|
||||||
|
|
|
|
||||||
i.fa.fa-question-circle.small(
|
i.fa.fa-question-circle.small(
|
||||||
ng-if="hasGenericOwnerName()"
|
ng-if="hasGenericOwnerName()"
|
||||||
|
|
|
@ -125,6 +125,7 @@ define([
|
||||||
$scope.settings = window.userSettings
|
$scope.settings = window.userSettings
|
||||||
$scope.anonymous = window.anonymous
|
$scope.anonymous = window.anonymous
|
||||||
$scope.isTokenMember = window.isTokenMember
|
$scope.isTokenMember = window.isTokenMember
|
||||||
|
$scope.isRestrictedTokenMember = window.isRestrictedTokenMember
|
||||||
|
|
||||||
$scope.cobranding = {
|
$scope.cobranding = {
|
||||||
isProjectCobranded: CobrandingDataService.isProjectCobranded(),
|
isProjectCobranded: CobrandingDataService.isProjectCobranded(),
|
||||||
|
|
|
@ -759,7 +759,8 @@ define(['base', 'main/project-list/services/project-list'], function(App) {
|
||||||
|
|
||||||
$scope.getUserName = ProjectListService.getUserName
|
$scope.getUserName = ProjectListService.getUserName
|
||||||
|
|
||||||
$scope.isOwner = () => window.user_id === $scope.project.owner._id
|
$scope.isOwner = () =>
|
||||||
|
$scope.project.owner && window.user_id === $scope.project.owner._id
|
||||||
|
|
||||||
$scope.$watch('project.selected', function(value) {
|
$scope.$watch('project.selected', function(value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
|
|
|
@ -804,6 +804,30 @@ describe('ProjectController', function() {
|
||||||
return this.ProjectController.loadEditor(this.req, this.res)
|
return this.ProjectController.loadEditor(this.req, this.res)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should add isRestrictedTokenMember', function(done) {
|
||||||
|
this.res.render = (pageName, opts) => {
|
||||||
|
opts.isRestrictedTokenMember.should.exist
|
||||||
|
opts.isRestrictedTokenMember.should.equal(false)
|
||||||
|
return done()
|
||||||
|
}
|
||||||
|
return this.ProjectController.loadEditor(this.req, this.res)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should set isRestrictedTokenMember to true under the right conditions', function(done) {
|
||||||
|
this.CollaboratorsHandler.userIsTokenMember.callsArgWith(2, null, true)
|
||||||
|
this.AuthorizationManager.getPrivilegeLevelForProject.callsArgWith(
|
||||||
|
3,
|
||||||
|
null,
|
||||||
|
'readOnly'
|
||||||
|
)
|
||||||
|
this.res.render = (pageName, opts) => {
|
||||||
|
opts.isRestrictedTokenMember.should.exist
|
||||||
|
opts.isRestrictedTokenMember.should.equal(true)
|
||||||
|
return done()
|
||||||
|
}
|
||||||
|
return this.ProjectController.loadEditor(this.req, this.res)
|
||||||
|
})
|
||||||
|
|
||||||
it('should render the closed page if the editor is closed', function(done) {
|
it('should render the closed page if the editor is closed', function(done) {
|
||||||
this.settings.editorIsOpen = false
|
this.settings.editorIsOpen = false
|
||||||
this.res.render = (pageName, opts) => {
|
this.res.render = (pageName, opts) => {
|
||||||
|
@ -996,6 +1020,83 @@ describe('ProjectController', function() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('_buildProjectViewModel', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
this.ProjectHelper.isArchived.returns(false)
|
||||||
|
this.project = {
|
||||||
|
_id: 'abcd',
|
||||||
|
name: 'netsenits',
|
||||||
|
lastUpdated: 1,
|
||||||
|
lastUpdatedBy: 2,
|
||||||
|
publicAccesLevel: 'private',
|
||||||
|
archived: false,
|
||||||
|
owner_ref: 'defg',
|
||||||
|
tokens: {
|
||||||
|
readAndWrite: '1abcd',
|
||||||
|
readAndWritePrefix: '1',
|
||||||
|
readOnly: 'neiotsranteoia'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should produce a model of the project', function() {
|
||||||
|
const result = this.ProjectController._buildProjectViewModel(
|
||||||
|
this.project,
|
||||||
|
'readAndWrite',
|
||||||
|
'owner',
|
||||||
|
this.user._id
|
||||||
|
)
|
||||||
|
expect(result).to.exist
|
||||||
|
expect(result).to.be.object
|
||||||
|
expect(result).to.deep.equal({
|
||||||
|
id: 'abcd',
|
||||||
|
name: 'netsenits',
|
||||||
|
lastUpdated: 1,
|
||||||
|
lastUpdatedBy: 2,
|
||||||
|
publicAccessLevel: 'private',
|
||||||
|
accessLevel: 'readAndWrite',
|
||||||
|
source: 'owner',
|
||||||
|
archived: false,
|
||||||
|
owner_ref: 'defg',
|
||||||
|
tokens: {
|
||||||
|
readAndWrite: '1abcd',
|
||||||
|
readAndWritePrefix: '1',
|
||||||
|
readOnly: 'neiotsranteoia'
|
||||||
|
},
|
||||||
|
isV1Project: false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when token-read-only access', function() {
|
||||||
|
it('should redact the owner and last-updated data', function() {
|
||||||
|
const result = this.ProjectController._buildProjectViewModel(
|
||||||
|
this.project,
|
||||||
|
'readOnly',
|
||||||
|
'token',
|
||||||
|
this.user._id
|
||||||
|
)
|
||||||
|
expect(result).to.exist
|
||||||
|
expect(result).to.be.object
|
||||||
|
expect(result).to.deep.equal({
|
||||||
|
id: 'abcd',
|
||||||
|
name: 'netsenits',
|
||||||
|
lastUpdated: 1,
|
||||||
|
lastUpdatedBy: null,
|
||||||
|
publicAccessLevel: 'private',
|
||||||
|
accessLevel: 'readOnly',
|
||||||
|
source: 'token',
|
||||||
|
archived: false,
|
||||||
|
owner_ref: null,
|
||||||
|
tokens: {
|
||||||
|
readAndWrite: '1abcd',
|
||||||
|
readAndWritePrefix: '1',
|
||||||
|
readOnly: 'neiotsranteoia'
|
||||||
|
},
|
||||||
|
isV1Project: false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
describe('_isInPercentageRollout', function() {
|
describe('_isInPercentageRollout', function() {
|
||||||
before(function() {
|
before(function() {
|
||||||
return (this.ids = [
|
return (this.ids = [
|
||||||
|
|
Loading…
Reference in a new issue