FullStory updates
This change 1. moves the feature flag from RootAccount to SiteAdmin, as it should not be manipulated by any of our institutions 2. hides the mobile login qr code from fullstory using their fs-exclude class name 3. hides the users' avatars by adding a data-fs-exclude attribute to all instances of the instui Avatar component, which we an then filter on in the fullstory UI (because instui doesn't let us add a className) 4. hides the users' avatars that are not Avatar by adding .fs-exclude Note: I am hiding all <img> elements via the FullStory UI, but this does not hide Avatar, which puts the image in as a background-image closes UXS-97 flag=enable_fullstory test plan: - you can really only test in beta where fullstory can capture sessions and see that user avatars and the qr code are hidden Change-Id: Ic8b73a2d7cd0474c1fd9a5337f747b76e5f67d06 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/232666 QA-Review: David Tan <dtan@instructure.com> Reviewed-by: Augusto Callejas <acallejas@instructure.com> Reviewed-by: Alex Anderson <raanderson@instructure.com> Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Product-Review: Ed Schiebel <eschiebel@instructure.com>
This commit is contained in:
parent
40e53e026b
commit
531b3a359a
|
@ -70,7 +70,7 @@ module Login::Shared
|
|||
session[:require_terms] = true if @domain_root_account.require_acceptance_of_terms?(user)
|
||||
@current_user = user
|
||||
|
||||
fullstory_init(@domain_root_account, session)
|
||||
fullstory_init(Account.site_admin, session)
|
||||
|
||||
respond_to do |format|
|
||||
if (oauth = session[:oauth2])
|
||||
|
|
|
@ -62,7 +62,7 @@ module AvatarHelper
|
|||
end
|
||||
end
|
||||
link_opts = {}
|
||||
link_opts[:class] = 'avatar '+opts[:class].to_s
|
||||
link_opts[:class] = 'fs-exclude avatar '+opts[:class].to_s
|
||||
link_opts[:style] = "background-image: url(#{avatar_url})"
|
||||
link_opts[:style] += ";width: #{opts[:size]}px;height: #{opts[:size]}px" if opts[:size]
|
||||
link_opts[:href] = url if url
|
||||
|
|
|
@ -27,7 +27,13 @@ export default function UserLink({size, avatar_url, name, avatarName, ...propsTo
|
|||
theme={{mediumPadding: '0', mediumHeight: '1rem'}}
|
||||
{...propsToPassOnToLink}
|
||||
>
|
||||
<Avatar size={size} name={avatarName} src={avatar_url} margin="0 x-small xxx-small 0" />
|
||||
<Avatar
|
||||
size={size}
|
||||
name={avatarName}
|
||||
src={avatar_url}
|
||||
margin="0 x-small xxx-small 0"
|
||||
data-fs-exclude
|
||||
/>
|
||||
{name}
|
||||
</Button>
|
||||
)
|
||||
|
|
|
@ -211,6 +211,7 @@ export default class ActAsModal extends React.Component {
|
|||
src={user.avatar_image_url}
|
||||
size="small"
|
||||
margin="medium 0 x-small 0"
|
||||
data-fs-exclude
|
||||
/>
|
||||
</View>
|
||||
<View as="div" textAlign="center">
|
||||
|
|
|
@ -112,6 +112,7 @@ exports[`ActAsModal renders with panda svgs, user avatar, table, and proceed but
|
|||
textAlign="center"
|
||||
>
|
||||
<Avatar
|
||||
data-fs-exclude={true}
|
||||
inline={true}
|
||||
margin="medium 0 x-small 0"
|
||||
name="foo"
|
||||
|
|
|
@ -54,6 +54,7 @@ export default function CommentRow(props) {
|
|||
name={author ? author.shortName : I18n.t('Anonymous')}
|
||||
src={author ? author.avatarUrl : ''}
|
||||
margin="0 small 0 0"
|
||||
data-fs-exclude
|
||||
/>
|
||||
</div>
|
||||
<div className="comment-text-comment-container">
|
||||
|
@ -89,4 +90,3 @@ export default function CommentRow(props) {
|
|||
CommentRow.propTypes = {
|
||||
comment: SubmissionComment.shape.isRequired
|
||||
}
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ export default class StudentTray extends React.Component {
|
|||
aria-label={I18n.t("Go to %{name}'s profile", {name})}
|
||||
target="_blank"
|
||||
>
|
||||
<Avatar size="x-large" name={name} src={this.props.student.avatarUrl} />
|
||||
<Avatar size="x-large" name={name} src={this.props.student.avatarUrl} data-fs-exclude />
|
||||
</Link>
|
||||
</View>
|
||||
)
|
||||
|
|
|
@ -105,7 +105,13 @@ export default class StudentsTable extends React.Component {
|
|||
const displayName = student.shortName || student.name || I18n.t('User')
|
||||
return (
|
||||
<>
|
||||
<Avatar name={student.name} src={student.avatarUrl} size="small" margin="0 small 0 0" />
|
||||
<Avatar
|
||||
name={student.name}
|
||||
src={student.avatarUrl}
|
||||
size="small"
|
||||
margin="0 small 0 0"
|
||||
data-fs-exclude
|
||||
/>
|
||||
{displayName}
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -151,6 +151,7 @@ export default function ReceivedTable({shares, onPreview, onImport, onRemove, on
|
|||
size="small"
|
||||
name={share.sender.display_name}
|
||||
src={share.sender.avatar_image_url}
|
||||
data-fs-exclude
|
||||
/>{' '}
|
||||
{share.sender.display_name}
|
||||
</Table.Cell>
|
||||
|
|
|
@ -44,7 +44,7 @@ class Avatar extends React.Component {
|
|||
href={`/courses/${this.props.courseId}/users/${user._id}`}
|
||||
aria-label={I18n.t("Go to %{name}'s profile", {name})}
|
||||
>
|
||||
<InstUIAvatar size="x-large" name={name} src={user.avatar_url} />
|
||||
<InstUIAvatar size="x-large" name={name} src={user.avatar_url} data-fs-exclude />
|
||||
</Link>
|
||||
{canMasquerade && (
|
||||
<Text size="x-small" weight="bold" as="div">
|
||||
|
|
|
@ -109,6 +109,7 @@ export default class SubmissionCommentListItem extends React.Component {
|
|||
alt={I18n.t('Avatar for %{author}', {author: this.props.author})}
|
||||
src={this.props.authorAvatarUrl}
|
||||
margin="0 x-small 0 0"
|
||||
data-fs-exclude
|
||||
/>
|
||||
</Link>
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ import {extractSimilarityInfo} from '../../../grading/helpers/SubmissionHelper'
|
|||
function renderAvatar(name, avatarUrl) {
|
||||
return (
|
||||
<div id="SubmissionTray__Avatar">
|
||||
<Avatar name={name} src={avatarUrl} size="auto" />
|
||||
<Avatar name={name} src={avatarUrl} size="auto" data-fs-exclude />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -137,6 +137,7 @@ export default class MobileGlobalMenu extends React.Component {
|
|||
name={this.props.current_user.display_name}
|
||||
src={this.props.current_user.avatar_image_url}
|
||||
size="x-small"
|
||||
data-fs-exclude
|
||||
/>
|
||||
</Flex.Item>
|
||||
<Flex.Item>
|
||||
|
|
|
@ -87,6 +87,7 @@ export default function ProfileTray(props) {
|
|||
size="x-large"
|
||||
inline={false}
|
||||
margin="auto"
|
||||
data-fs-exclude
|
||||
/>
|
||||
<div style={{wordBreak: 'break-word'}}>
|
||||
<Heading level="h3" as="h2">
|
||||
|
|
|
@ -51,7 +51,9 @@ export function QRLoginModal({onDismiss, refreshInterval, pollInterval}) {
|
|||
|
||||
function renderQRCode() {
|
||||
const body = imagePng ? (
|
||||
<Img data-testid="qr-code-image" src={`data:image/png;base64, ${imagePng}`} />
|
||||
<span className="fs-exclude">
|
||||
<Img data-testid="qr-code-image" src={`data:image/png;base64, ${imagePng}`} />
|
||||
</span>
|
||||
) : (
|
||||
<Spinner
|
||||
data-testid="qr-code-spinner"
|
||||
|
|
|
@ -225,6 +225,7 @@ export default class CourseItemRow extends Component {
|
|||
size="small"
|
||||
name={this.props.author.display_name || I18n.t('Unknown')}
|
||||
src={this.props.author.avatar_image_url}
|
||||
data-fs-exclude
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -223,6 +223,7 @@ export default class CreateOrUpdateUserModal extends React.Component {
|
|||
size="small"
|
||||
name={this.state.data.user.name}
|
||||
src={this.props.user.avatar_url}
|
||||
data-fs-exclude
|
||||
/>{' '}
|
||||
{I18n.t('Edit User Details')}
|
||||
</span>
|
||||
|
|
|
@ -33,7 +33,7 @@ export default function UserSearchSelectorItem({user}) {
|
|||
return (
|
||||
<Flex>
|
||||
<Flex.Item>
|
||||
<Avatar name={name} src={avatar_url} />
|
||||
<Avatar name={name} src={avatar_url} data-fs-exclude />
|
||||
</Flex.Item>
|
||||
<Flex.Item padding="0 0 0 small" grow>
|
||||
<Flex direction="column">
|
||||
|
|
|
@ -1,27 +1,20 @@
|
|||
{{!-- same markup as ApplicationHelper#avatar, essensially --}}
|
||||
{{#if this}}
|
||||
{{#if anonymous}}
|
||||
<a
|
||||
class="avatar"
|
||||
style="background-image: url(/images/messages/avatar-50.png)"
|
||||
>
|
||||
<span class="screenreader-only">{{#t}}Student{{/t}}</span>
|
||||
</a>
|
||||
{{else}}
|
||||
{{#ifAny avatar_url avatar_image_url}}
|
||||
{{#if alreadyEscaped}}
|
||||
<a
|
||||
{{#ifAny url html_url}}href="{{{or url html_url}}}"{{/ifAny}}
|
||||
class="avatar"
|
||||
style="background-image: url({{{or avatar_url avatar_image_url}}})">
|
||||
<span class="screenreader-only">{{{or display_name short_name}}}</span></a>
|
||||
{{else}}
|
||||
<a
|
||||
{{#ifAny url html_url}}href="{{or url html_url}}"{{/ifAny}}
|
||||
class="avatar"
|
||||
style="background-image: url({{or avatar_url avatar_image_url}})"
|
||||
><span class="screenreader-only">{{or display_name short_name}}</span></a>
|
||||
{{/if}}
|
||||
{{/ifAny}}
|
||||
{{/if}}
|
||||
{{#if anonymous}}
|
||||
<a class="avatar" style="background-image: url(/images/messages/avatar-50.png)">
|
||||
<span class="screenreader-only">{{#t}}Student{{/t}}</span>
|
||||
</a>
|
||||
{{else}}
|
||||
{{#ifAny avatar_url avatar_image_url}}
|
||||
{{#if alreadyEscaped}}
|
||||
<a {{#ifAny url html_url}}href="{{{or url html_url}}}" {{/ifAny}} class="fs-exclude avatar"
|
||||
style="background-image: url({{{or avatar_url avatar_image_url}}})">
|
||||
<span class="screenreader-only">{{{or display_name short_name}}}</span></a>
|
||||
{{else}}
|
||||
<a {{#ifAny url html_url}}href="{{or url html_url}}" {{/ifAny}} class="fs-exclude avatar"
|
||||
style="background-image: url({{or avatar_url avatar_image_url}})"><span
|
||||
class="screenreader-only">{{or display_name short_name}}</span></a>
|
||||
{{/if}}
|
||||
{{/ifAny}}
|
||||
{{/if}}
|
||||
{{/if}}
|
|
@ -1,9 +1,9 @@
|
|||
<td class="center">
|
||||
<a href="{{html_url}}" class="avatar" title="{{name}}"><img src="{{avatar_url}}" alt="{{name}}"></a>
|
||||
<a href="{{html_url}}" class="fs-exclude avatar" title="{{name}}"><img src="{{avatar_url}}" alt="{{name}}"></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{html_url}}" data-user-id="{{id}}" class="roster_user_name">{{name}}</a>
|
||||
</td>
|
||||
<td>
|
||||
{{login_id}}
|
||||
</td>
|
||||
</td>
|
|
@ -99,7 +99,6 @@
|
|||
<title><%= @page_title || (yield :page_title).presence || t('default_page_title', "Canvas LMS") %></title>
|
||||
<%= render partial: 'layouts/google_analytics_snippet' %>
|
||||
|
||||
<!-- fullstory snippet -->
|
||||
<% if fullstory_enabled_for_session?(session) %>
|
||||
<%= render :partial => "shared/fullstory_snippet" %>
|
||||
<% end %>
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
<%
|
||||
fullstory_key = fullstory_app_key
|
||||
if (fullstory_key) %>
|
||||
<!-- fullstory snippet -->
|
||||
<script>
|
||||
window['_fs_debug'] = false;
|
||||
window['_fs_host'] = 'fullstory.com';
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
<li class="menu-item ic-app-header__menu-list-item <%= ' ic-app-header__menu-list-item--active' if active_path?('/profile') %>">
|
||||
<a id="global_nav_profile_link" role="button" href="/profile" class="ic-app-header__menu-list-link">
|
||||
<div class="menu-item-icon-container">
|
||||
<div aria-hidden="true" class="ic-avatar <% if @real_current_user && @real_current_user != @current_user %>ic-avatar--fake-student<% end %>">
|
||||
<div aria-hidden="true" class="fs-exclude ic-avatar <% if @real_current_user && @real_current_user != @current_user %>ic-avatar--fake-student<% end %>">
|
||||
<img src="<%= @current_user.try { |usr| avatar_image_attrs(usr).first } %>" alt="<%= @current_user.short_name %>" />
|
||||
</div>
|
||||
<span class="menu-item__badge"></span>
|
||||
|
|
|
@ -62,4 +62,4 @@ enable_fullstory:
|
|||
display_name: Enable fullstory
|
||||
description: |-
|
||||
Include FullStory recording of the user's session
|
||||
applies_to: RootAccount
|
||||
applies_to: SiteAdmin
|
||||
|
|
|
@ -316,6 +316,7 @@ exports[`prefers to render feedback if it and the location are available 1`] = `
|
|||
className="PlannerItem-styles__feedbackAvatar"
|
||||
>
|
||||
<Avatar
|
||||
data-fs-exclude={true}
|
||||
inline={true}
|
||||
name="Dr. David Bowman"
|
||||
onImageLoaded={[Function]}
|
||||
|
@ -3332,6 +3333,7 @@ exports[`renders Note correctly with everything 1`] = `
|
|||
}
|
||||
>
|
||||
<Avatar
|
||||
data-fs-exclude={true}
|
||||
inline={true}
|
||||
name="Jane"
|
||||
onImageLoaded={[Function]}
|
||||
|
@ -3521,6 +3523,7 @@ exports[`renders Note correctly without Course 1`] = `
|
|||
}
|
||||
>
|
||||
<Avatar
|
||||
data-fs-exclude={true}
|
||||
inline={true}
|
||||
name="Jane"
|
||||
onImageLoaded={[Function]}
|
||||
|
@ -5425,6 +5428,7 @@ exports[`renders feedback anonymously according to the assignment settings 1`] =
|
|||
className="PlannerItem-styles__feedbackAvatar"
|
||||
>
|
||||
<Avatar
|
||||
data-fs-exclude={true}
|
||||
inline={true}
|
||||
name="?"
|
||||
onImageLoaded={[Function]}
|
||||
|
@ -5600,6 +5604,7 @@ exports[`renders feedback if available 1`] = `
|
|||
className="PlannerItem-styles__feedbackAvatar"
|
||||
>
|
||||
<Avatar
|
||||
data-fs-exclude={true}
|
||||
inline={true}
|
||||
name="Boyd Crowder"
|
||||
onImageLoaded={[Function]}
|
||||
|
@ -5776,6 +5781,7 @@ exports[`renders media feedback if available 1`] = `
|
|||
className="PlannerItem-styles__feedbackAvatar"
|
||||
>
|
||||
<Avatar
|
||||
data-fs-exclude={true}
|
||||
inline={true}
|
||||
name="Howard Stern"
|
||||
onImageLoaded={[Function]}
|
||||
|
@ -6178,6 +6184,7 @@ exports[`renders user-created Todo correctly 1`] = `
|
|||
}
|
||||
>
|
||||
<Avatar
|
||||
data-fs-exclude={true}
|
||||
inline={true}
|
||||
name="Jane"
|
||||
onImageLoaded={[Function]}
|
||||
|
|
|
@ -282,7 +282,12 @@ export class PlannerItem extends Component {
|
|||
return <IconPeerReviewLine />
|
||||
default:
|
||||
return (
|
||||
<Avatar name={currentUser.displayName || '?'} src={currentUser.avatarUrl} size="small" />
|
||||
<Avatar
|
||||
name={currentUser.displayName || '?'}
|
||||
src={currentUser.avatarUrl}
|
||||
size="small"
|
||||
data-fs-exclude
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -480,6 +485,7 @@ export class PlannerItem extends Component {
|
|||
name={feedback.author_name || '?'}
|
||||
src={feedback.author_avatar_url}
|
||||
size="small"
|
||||
data-fs-exclude
|
||||
/>
|
||||
</span>
|
||||
<span className={styles.feedbackComment}>
|
||||
|
|
|
@ -112,7 +112,7 @@ function Attribution({name, avatarUrl, profileUrl}) {
|
|||
return (
|
||||
<Flex>
|
||||
<Flex.Item margin="xx-small">
|
||||
<Avatar name={name} src={avatarUrl} size="small" />
|
||||
<Avatar name={name} src={avatarUrl} size="small" data-fs-exclude />
|
||||
</Flex.Item>
|
||||
<Flex.Item margin="xx-small" shrink>
|
||||
<Button
|
||||
|
|
|
@ -22,19 +22,19 @@ require 'spec_helper'
|
|||
include FullStoryHelper
|
||||
|
||||
before :each do
|
||||
@domain_root_account = Account.default
|
||||
@site_admin_account = Account.site_admin
|
||||
@session = {}
|
||||
end
|
||||
|
||||
context "with feature enabled" do
|
||||
before :each do
|
||||
@domain_root_account.enable_feature!(:enable_fullstory)
|
||||
@site_admin_account.enable_feature!(:enable_fullstory)
|
||||
end
|
||||
|
||||
it 'is enabled if login is sampled' do
|
||||
allow(FullStoryHelper).to receive(:rand).and_return(0.5)
|
||||
override_dynamic_settings(config: {canvas: { fullstory: {sampling_rate: 1, app_key: '12345'} } }) do
|
||||
fullstory_init(@domain_root_account, @session)
|
||||
fullstory_init(@site_admin_account, @session)
|
||||
expect(fullstory_app_key).to eql('12345')
|
||||
expect(@session[:fullstory_enabled]).to be_truthy
|
||||
expect(fullstory_enabled_for_session?(@session)).to be_truthy
|
||||
|
@ -44,7 +44,7 @@ require 'spec_helper'
|
|||
it 'is disabled if login is not sampled' do
|
||||
allow(FullStoryHelper).to receive(:rand).and_return(0.5)
|
||||
override_dynamic_settings(config: {canvas: { fullstory: {sampling_rate: 0, app_key: '12345'} } }) do
|
||||
fullstory_init(@domain_root_account, @session)
|
||||
fullstory_init(@site_admin_account, @session)
|
||||
expect(fullstory_app_key).to eql('12345')
|
||||
expect(@session[:fullstory_enabled]).to be_falsey
|
||||
expect(fullstory_enabled_for_session?(@session)).to be_falsey
|
||||
|
@ -54,7 +54,7 @@ require 'spec_helper'
|
|||
it "doesn't explode if the dynamic settings are missing" do
|
||||
allow(FullStoryHelper).to receive(:rand).and_return(0.5)
|
||||
override_dynamic_settings(config: {canvas: { fullstory: nil } }) do
|
||||
fullstory_init(@domain_root_account, @session)
|
||||
fullstory_init(@site_admin_account, @session)
|
||||
expect(fullstory_app_key).to be_nil
|
||||
expect(@session[:fullstory_enabled]).to be_falsey
|
||||
expect(fullstory_enabled_for_session?(@session)).to be_falsey
|
||||
|
@ -64,13 +64,13 @@ require 'spec_helper'
|
|||
|
||||
context "with feature disabled" do
|
||||
before :each do
|
||||
@domain_root_account.disable_feature!(:enable_fullstory)
|
||||
@site_admin_account.disable_feature!(:enable_fullstory)
|
||||
end
|
||||
|
||||
it 'is disabled' do
|
||||
allow(FullStoryHelper).to receive(:rand).and_return(0.5)
|
||||
override_dynamic_settings(config: {canvas: { fullstory: {sampling_rate: 1, app_key: '12345'} } }) do
|
||||
fullstory_init(@domain_root_account, @session)
|
||||
fullstory_init(@site_admin_account, @session)
|
||||
expect(fullstory_app_key).to eql('12345')
|
||||
expect(@session[:fullstory_enabled]).to be_falsey
|
||||
expect(fullstory_enabled_for_session?(@session)).to be_falsey
|
||||
|
|
Loading…
Reference in New Issue