import _filter from 'lodash.filter'; import { CONTENT_CONSTANTS } from './content.constant'; import { CONTRIBUTOR_SOURCE } from './contributorSource.contant'; import { MEDIA_TYPE } from './mediaType.constant'; import { FACET_NAME } from './facetName.constant'; class Content { constructor(content) { const restPath = ContentFactory.Paths.get('REST'); angular.extend(this, content || {}); this.contentItems = (this.contentItems || []).map(item => new Content(item)); if (this.associatedTools && this.associatedTools.contentItems) { this.associatedTools.contentItems = this.associatedTools.contentItems.map(item => new Content(item)); } if (this.associatedTeacherSupport && this.associatedTeacherSupport.contentItems) { this.associatedTeacherSupport.contentItems = this.associatedTeacherSupport.contentItems .map(item => new Content(item)); } if (!this.metrics) { this.metrics = { uuid: null, version: 1, visitCount: 0, commentCount: 0, }; } this.buildFromDetailsTree(); } static query(params, less, cache) { ContentFactory.$log.log('Content.query called', params); const promise = ContentFactory.api.get(less ? 'items' : 'content', { params, config: { cache: !!cache }, }) .then((response) => { response.data.results = Content.getListWithCustomizedTestInserted(response.data.results); return response; }) .then(convertQueryResults) .then(updateQueryResultsTeacherOnly) .then(result => (params.STATE_STANDARD ? processStandardDescription(result) : result)); return promise; } static queryFast(params, success, error) { return ContentFactory.api.get('searchFast', { params, }) .then((response) => { const results = []; angular.forEach(response.data, (item) => { results.push(new Content(item)); }); success(results); return results; }, error); } static getETexts(productIdQuery, cache) { return ContentFactory.api.get('searchFast', { params: { OLE_PRODUCT_ID: productIdQuery, pageSize: '20', MEDIA_TYPE: MEDIA_TYPE.ETEXT, NOT_ITEM_STATUS: [CONTENT_CONSTANTS.NOT_ITEM_STATUS.DELETED, CONTENT_CONSTANTS.NOT_ITEM_STATUS.ARCHIVED], }, config: { cache: !!cache }, }).then((response) => { const results = []; response.data.forEach((etext) => { results.push(new Content(etext)); }); return results; }, err => ContentFactory.$q.reject(err)); } static getToolsByPrograms(productIds, cache) { const promise = ContentFactory.api.get('tools', { params: { programs: productIds, }, config: { cache: !!cache }, }).then((response) => { const tools = []; response.data.forEach((tool) => { tools.push(new Content(tool)); }); return tools; }, err => ContentFactory.$q.reject(err)); return promise; } static setStudentCenterData(params) { let promise = null; promise = ContentFactory.api.post('studentCenters', params) .then(response => response.data); return promise; } /** * @param Boolean less * when true, call does not return empty or null fields of items * Note: get a single content item */ static get(params, less, cache, skipInsertCustomized) { let future = {}; let promise = null; const paramsClone = Object.assign(params); if (angular.isUndefined(paramsClone.levels)) { paramsClone.levels = 1; } promise = ContentFactory.api.get(less ? 'singleItem' : 'singleContent', { params: paramsClone, config: { cache: !!cache }, }) .then((response) => { processStandardDescription(response.data); return response; }) .then((response) => { if (!skipInsertCustomized) { const isCustomizedSequence = response.data.fileType === CONTENT_CONSTANTS.FILE_TYPE.SEQUENCE && Content.hasCustomizedItem(response.data); if (isCustomizedSequence) { response.data.customizedItem.contentItems = Content .getListWithCustomizedTestInserted(response.data.customizedItem.contentItems); } response.data.contentItems = Content.getListWithCustomizedTestInserted(response.data.contentItems); } return response; }) .then((response) => { future = angular.assign({}, new Content(response.data)); if (future.fileType === CONTENT_CONSTANTS.FILE_TYPE.SEQUENCE) { const filterFunction = item => item.externalSource === CONTENT_CONSTANTS.EXTERNALSOURCE.OPEN_ED; return OpenEdItem.retrieveItemsInList(future.contentItems, filterFunction) .then(() => future.updateTeacherOnly()); } else if (future.externalSource === CONTENT_CONSTANTS.EXTERNALSOURCE.OPEN_ED) { const originalEquellaItemId = future.id; return OpenEdItem.getPromise(future.externalId).then((item) => { future = new Content(item); future.id = originalEquellaItemId; return future.updateTeacherOnly(); }); } return future.updateTeacherOnly(); }); return promise; } static getWithoutInsertingCustomized(params, less, cache) { const skipInsertCustomized = true; return Content.get(params, less, cache, skipInsertCustomized); } static hasCustomizedItem(item) { return item.customizedItem && item.customizedItemDefaultView; } static isHiddenViaCustomize(item) { return item.associativeProps && item.associativeProps.externalSource === CONTENT_CONSTANTS.EXTERNALSOURCE.HIDE_FROM_STUDENT; } hasAssociatedItem(name) { const item = this; const key = getJsonKeyForAssociatedItem(name); return item[key] && !!item[key].id && !!item[key].version; } setAssociatedItem(name, less, cache) { const item = this; const key = getJsonKeyForAssociatedItem(name); const deferredAssociatedItem = ContentFactory.$q.defer(); if (item.hasAssociatedItem(name)) { Content.get({ contentId: item[key].id, version: item[key].version, }, less, cache).then((associatedItem) => { item[key] = associatedItem; deferredAssociatedItem.resolve(); }, () => { ContentFactory.$q.reject(); }); } else { deferredAssociatedItem.resolve(); } return deferredAssociatedItem.promise; } static getRoute(item, program, tier, lesson) { /** in this method "tier" may refer to an actual Tier or a Lesson * we need all this logic JUST so we can have a little arrow that says back to whatever */ if (program && tier && lesson) { /** * this case there are no nested lessons, so tier must be a tier, and item must be general content */ return `/program/${program.id}/${program.version}/tier/${tier.id}/${tier.version}/lesson/${lesson.id} /${lesson.version}/content/${item.id}/${item.version}`; } else if (program && tier) { if (item.editAssessment) { return `/program/${program.id}/${program.version}/${tier.mediaType.toLowerCase()}/ ${tier.id}/${tier.version}/assessment/${item.id}/${item.version}/edit`; } if (item.mediaType === MEDIA_TYPE.LESSON) { return `/program/${program.id}/${program.version}/ ${tier.mediaType.toLowerCase()}/${tier.id}/${tier.version}/lesson/${item.id}/${item.version}`; } return `/program/${program.id}/${program.version}/ ${tier.mediaType.toLowerCase()}/${tier.id}/${tier.version}/content/${item.id}/${item.version}`; } else if (program) { if (item.editAssessment) { return `/program/${program.id}/${program.version}/assessment/${item.id}/${item.version}/edit`; } const tierDelim = item.mediaType === MEDIA_TYPE.LESSON || item.mediaType === MEDIA_TYPE.TIER ? item.mediaType.toLowerCase() : 'content'; return `/program/${program.id}/${program.version}/${tierDelim}/${item.id}/${item.version}`; } else if (item.essayPrompt) { return `/assessment/essay/${item.id}/${item.version}/edit`; } else if (item.mediaType === MEDIA_TYPE.TEST) { return `/assessment/${item.id}/${item.version}/edit`; } else if (item.mediaType === MEDIA_TYPE.TIER && item.root) { return `/program/${item.id}/${item.version}`; } else if (item.mediaType === MEDIA_TYPE.LESSON) { return `/lesson/${item.id}/${item.version}/edit`; } return `/content/${item.id}/${item.version}`; } static publish(contentItem, callback) { ContentFactory.api.put('publishedContent', { params: { contentItemId: contentItem.id, contentItemVersion: contentItem.version, }, }, ) .success((response) => { const allProgramsUrl = `${restPath}/programs; const allCentersUrl = ${restPath}/centers; const cache = ContentFactory.$cacheFactory.get('$http'); cache.remove(allProgramsUrl); cache.remove(allCentersUrl); if (response) { callback(); } }); } static getAssessment(aId, success) { return ContentFactory.api.get('assessment', { params: { assessmentId: aId + '.json' }, }) .then(setSkillsLocaleDescription) .success(success) .error((data) => { ContentFactory.$log.error('failed to retrieve assessment', data.errorCode, data.errorMessage); }); } static getSkillsById(ids, success) { return ContentFactory.api.get('skills', { params: { 'ids[]': ids, }, }) .success(success) .error((data) => { ContentFactory.$log.error('failed to fetch skill details', data.errorCode, data.errorMessage); }); } static updateMultiValueFacets(params) { const paramsClone = Object.assign({}, params); const facetNames = [FACET_NAME.FEATURED_RESOURCES, FACET_NAME.GRADE, FACET_NAME.LEARNING_MODEL, FACET_NAME.LIBRARY_TITLE, FACET_NAME.SUBTOPIC, FACET_NAME.COMPREHENSION_SKILLS, FACET_NAME.TEXT_FEATURES, FACET_NAME.GENRES, FACET_NAME.CONTENT_AREAS]; angular.forEach(facetNames, (name) => { if (paramsClone[name] && paramsClone[name].length !== 0) { const joinedValues = paramsClone[name].join(','); params[name] = `ALL(${joinedValues})`; } }); } static getLessonTOCRoute(programId, programVersion, UUID) { return `/program/${programId}/${programVersion}/tier/${UUID}/0`; } static getTeacherOnlyValues(itemUuids, success) { ContentFactory.$log.log('getTeacherOnlyValues called', itemUuids); const promise = ContentFactory.api.get('teacherOnly', { params: { itemUuids, }, config: { cache: true }, }); promise.success((response) => { ContentFactory.$log.log('getTeacherOnlyValues success', response); }) .error((data) => { ContentFactory.$log.error('failed to fetch teacherOnly values', data.errorCode, data.errorMessage); }); if (angular.isDefined(success) && angular.isFunction(success)) { promise.success(success); } return promise; } static getContentStandards(id, version) { const deferred = ContentFactory.$q.defer(); Content.get({ contentId: id, version, }, false, true) .then((response) => { const program = response; program.libraryDelimited = program.library.join('|'); Standard.getStandardTree(program.libraryDelimited, program) .then((tree) => { deferred.resolve({ program, standards: tree, }); }, deferred.reject, ); }, deferred.reject, ); return deferred.promise; } static getQuestionBankStandards(id, version) { const deferred = ContentFactory.$q.defer(); Content.get({ contentId: id, version, }, false, true) .then((response) => { const program = response; program.libraryDelimited = program.library.join('|'); Standard.getQuestionBankStandardTree(program.libraryDelimited, program) .then((tree) => { deferred.resolve({ program, standards: tree, }); }, deferred.reject, ); }, deferred.reject, ); return deferred.promise; } static hasRemediation(item) { return item.hasRemediation(); } static getListWithCustomizedTestInserted(contentArray) { const resultArray = []; angular.forEach(contentArray, (contentItem) => { const contentItemClone = Object.assign(contentItem); if (contentItemClone.mediaType === MEDIA_TYPE.TEST && Content.hasCustomizedItem(contentItemClone) && !contentItemClone.hasCustomizedVersion) { contentItemClone.hasCustomizedVersion = true; resultArray.push(contentItemClone); const customizedItem = contentItemClone.customizedItem; customizedItem.isCustomizedVersion = true; const originalCopy = angular.assign({}, contentItemClone); delete originalCopy.customizedItem; customizedItem.originalItem = originalCopy; resultArray.push(customizedItem); } else if (contentItemClone.fileType === MEDIA_TYPE.SEQUENCE && contentItemClone.contentItems && contentItemClone.contentItems.length > 0) { const childItems = contentItemClone.contentItems; contentItemClone.contentItems = Content.getListWithCustomizedTestInserted(childItems); resultArray.push(contentItemClone); } else { resultArray.push(contentItemClone); } }); return resultArray; } isLastRow(index, contentItemsLength, sidebarOpen) { let columnCount; if (sidebarOpen && !ContentFactory.MediaQuery.breakpoint.isDesktop) { columnCount = 2; } else if (sidebarOpen || !ContentFactory.MediaQuery.breakpoint.isDesktop) { columnCount = 3; } else { columnCount = 4; } const lastRowItemCount = contentItemsLength % columnCount; if (lastRowItemCount === 0) { return (index + 1) > (contentItemsLength - columnCount); } return (index + 1) > (contentItemsLength - lastRowItemCount); } updateTeacherOnly() { const self = this; const unknownIds = []; let yesCount = 0; const deferred = ContentFactory.$q.defer(); let i; let len; if (self.contentItems && self.contentItems.length > 0) { for (i = 0, len = self.contentItems.length; i < len; i += 1) { const child = self.contentItems[i]; if (!child.contentItems && child.teacherOnly === CONTENT_CONSTANTS.TEACHER_ONLY.IS_TEACHER_ONLY) { if (self.teacherOnly !== CONTENT_CONSTANTS.TEACHER_ONLY.IS_TEACHER_ONLY) { self.teacherOnly = 'No'; } } if (child.teacherOnly.toUpperCase() === CONTENT_CONSTANTS.TEACHER_ONLY.UNKNOWN) { unknownIds.push(child.id); } if (child.teacherOnly.toUpperCase() === CONTENT_CONSTANTS.TEACHER_ONLY.IS_TEACHER_ONLY) { yesCount += 1; } } if (yesCount === self.contentItems.length) { self.teacherOnly = 'Yes'; } if (unknownIds.length > 0) { Content.getTeacherOnlyValues(unknownIds) .success((values) => { Object.keys(values).map((key) => { let item = self.contentItems; item.find(contentItem => contentItem.id === value); item = item.length ? item[0] : null; if (item !== null) { item.teacherOnly = values[key] ? 'Yes' : 'No'; } return key; }); deferred.resolve(self); }); } else { deferred.resolve(self); } } else if (self.teacherOnly === CONTENT_CONSTANTS.TEACHER_ONLY.UNKNOWN) { Content.getTeacherOnlyValues([self.id]) .success((values) => { self.teacherOnly = values[self.id] ? 'Yes' : 'No'; deferred.resolve(self); }); } else { deferred.resolve(self); } return deferred.promise; } /** * Get the title for an item. If an active customized version exists, will fetch that title instead. * */ getTitle(forceVersion) { let item; let title; const forceOriginal = forceVersion === 'original'; const forceLatest = forceVersion === 'latest'; if (forceLatest) { item = this.getDefaultVersion(forceLatest); } else { item = forceOriginal ? this : this.getDefaultVersion(); } if (item.associativeProps && item.associativeProps.titleInSequence) { title = item.associativeProps.titleInSequence; } else { title = item.title; } return title; } getDescription() { const item = this.getDefaultVersion(); return item.text; } getEquellaItemId() { return this.id; } getDefaultVersion(forceLatest) { const item = this; const hasCustomizedItem = Content.hasCustomizedItem(item); if (!forceLatest && this.isTest() && !this.myContent) { return item; } return hasCustomizedItem ? new Content(item.customizedItem) : item; } getUrl() { let url = this.url; if (!(/^(http|https):\/\//.test(url))) { url = `http://${url}`; } if (this.mediaType === MEDIA_TYPE.PARTNER_LINK) { url = `rest/externaltool/content/view/${this.id}/${this.version}?launchUrl=${url}`; if (ContentFactory.$rootScope.currentProgram) { url = `${url}&programId=${ContentFactory.$rootScope.currentProgram.id} &programVersion=${ContentFactory.$rootScope.currentProgram.version}`; } } return url; } hasRemediation() { const item = this; return !!item.getDefaultVersion().remediationProperties && (item.fileType === CONTENT_CONSTANTS.FILE_TYPE.TEST || item.fileType === CONTENT_CONSTANTS.FILE_TYPE.SCO || item.fileType === CONTENT_CONSTANTS.FILE_TYPE.TIN_CAN_SCO); } hasRemediationActivities() { const item = this; return item.remediationSkills && !ContentFactory.ObjectUtilities.isEmpty(item.remediationSkills); } hasInfo() { const cleanTags = []; angular.forEach(this.tags, (tag) => { if (tag.trim().length > 0) { cleanTags.push(tag); } }); this.tags = cleanTags; if (this.fileType === CONTENT_CONSTANTS.FILE_TYPE.ETEXT) { return this.text.trim().length > 0; } const infoItemHasContent = (infoItem) => { const isEmptyArray = angular.isArray(infoItem) && (infoItem.join('').trim().length === 0); const isEmptyString = angular.isString(infoItem) && infoItem.trim().length === 0; if (isEmptyArray || isEmptyString || !infoItem) { return false; } return true; }; const item = this; const canHazText = infoItemHasContent(item.text); const canHazTags = infoItemHasContent(item.tags); const canHazMaterials = infoItemHasContent(item.materials); const canHazStandards = infoItemHasContent(item.standards); const canHazPacing = infoItemHasContent(item.pacing); const canHazAuthor = infoItemHasContent(item.author); const canHazIsbn = infoItemHasContent(item.isbn); const canHazLexile = infoItemHasContent(item.lexile); const canHazGR = infoItemHasContent(item.guidedReading); const canHazRMM = infoItemHasContent(item.readingMaturityMetric); const canHazDRA = infoItemHasContent(item.developmentalReadingAssessment); const canHazQuantile = infoItemHasContent(item.quantile); const canHazCompSkills = infoItemHasContent(item.comprehensionSkills); const canHazTextFeatures = infoItemHasContent(item.textFeatures); const canHazGenres = infoItemHasContent(item.genres); const canHazContentAreas = infoItemHasContent(item.contentAreas); const isStudent = ContentFactory.$rootScope.currentUser.isStudent; return canHazText || canHazTags || canHazStandards || (canHazMaterials && !isStudent) || (canHazPacing && !isStudent) || (canHazAuthor && !isStudent) || (canHazIsbn && !isStudent) || (canHazLexile && !isStudent) || (canHazGR && !isStudent) || (canHazRMM && !isStudent) || (canHazDRA && !isStudent) || (canHazQuantile && !isStudent) || (canHazCompSkills && !isStudent) || (canHazTextFeatures && !isStudent) || (canHazGenres && !isStudent) || (canHazContentAreas && !isStudent); } isCustomized() { const item = this; return item.customizedItem && item.customizedItemDefaultView; } getCustomizeContribDate() { const item = this; return item.getDefaultVersion().contribDate; } isPearsonItem() { const self = this; return self.contribSource === CONTRIBUTOR_SOURCE.PEARSON; } isAssignable() { const item = this; if (item.teacherOnly === CONTENT_CONSTANTS.TEACHER_ONLY.UNKNOWN) { ContentFactory.$log.log('Unresolved unknown teacherOnly', item); } return ContentFactory.$rootScope.currentUser.hasRole('ROLE_TEACHER') && item.teacherOnly === CONTENT_CONSTANTS.TEACHER_ONLY.NOT_TEACHER_ONLY && item.mediaType !== MEDIA_TYPE.TIER && item.mediaType !== MEDIA_TYPE.CENTER; } isPublishable() { const item = this; return ContentFactory.$rootScope.currentUser.isLibraryAdmin && !item.isCustomized(); } isExternalResource() { const self = this; return self.isOpenEdItem() || self.isGooruItem(); } getThumbnailUrl(page, showLarge) { const item = this; if (item.isOpenEdItem()) { return item.thumbnailUrls.length ? item.thumbnailUrls[0] : undefined; } if ((!item.thumbnailLocation && item.root) || (!item.thumbnailUrls && !item.root)) { return undefined; } let thumbnailPath = ''; let thumbnailName = ''; let thumbnailUrl = ''; let expectedThumbnailName = ''; let retinaThumbnailName = ''; let nonRetinaThumbnailName = ''; let fallbackThumbnailName = ''; let uxPath = ''; let retinaExtension = ''; const defaultRetina = '@2x'; let thumbnailExtension = ''; const defaultExtension = '.png'; const pagePosition = { HOME: 'HOME', PROGRAM_SUBNAV: 'PROGRAM_SUBNAV', TIER: 'TIER', }; if (ContentFactory.browserInfo.isHDDisplay) { retinaExtension = '@2x'; } if (item.root) { const isFullPathToThumbnail = item.thumbnailLocation.search(/^http(s)?|\//) === 0; if (!isFullPathToThumbnail) { item.thumbnailLocation = `${ContentFactory.Paths.get('SHARED_THUMBNAILS')}/${item.thumbnailLocation}`; } thumbnailPath = ContentFactory.$filter('removeExtension')(item.thumbnailLocation); if (page === pagePosition.HOME) { uxPath = (showLarge) ? '_homelarge' : '_homesmall'; } else if (page === pagePosition.PROGRAM_SUBNAV') { uxPath = '_dropdown'; } else { uxPath = '_course'; } thumbnailExtension = ContentFactory.$filter('getExtension')(item.thumbnailLocation, defaultExtension); return thumbnailPath + uxPath + retinaExtension + thumbnailExtension; } if (page === pagePosition.TIER) { if (showLarge) { uxPath = '_grid'; } } thumbnailName = ContentFactory.$filter('getFileName')(item.thumbnailUrls[0]); thumbnailExtension = ContentFactory.$filter('getExtension')(item.thumbnailUrls[0], defaultExtension); expectedThumbnailName = thumbnailName + uxPath + retinaExtension + thumbnailExtension; retinaThumbnailName = thumbnailName + uxPath + defaultRetina + thumbnailExtension; nonRetinaThumbnailName = thumbnailName + uxPath + thumbnailExtension; thumbnailUrl = item.thumbnailUrls.find(url => url.search(expectedThumbnailName) > -1); if (!thumbnailUrl) { fallbackThumbnailName = ContentFactory.browserInfo.isHDDisplay ? nonRetinaThumbnailName : retinaThumbnailName; thumbnailUrl = item.thumbnailUrls.find(url => url.search(fallbackThumbnailName) > -1); } return thumbnailUrl; } isOpenEdItem() { return isExternalItem(CONTENT_CONSTANTS.FILE_TYPE.OPEN_ED, this); } getValidFromDetails() { const item = this; if (!item.fromDetailsTrees) { return []; } return item.fromDetailsTrees.filter(fromDetail => fromDetail.distanceFrom === 1 && (fromDetail.containerMediaType === 'Program' || fromDetail.containerMediaType === 'Tier' || fromDetail.containerMediaType === 'Lesson' || fromDetail.containerMediaType === 'Learning Model')); } buildFromDetailsTree() { const self = this; const trees = []; let currentTree = null; let workingTree = null; let lastDistance = 100; const details = self.fromDetails; angular.forEach(details, (detail) => { const contentDetail = Object.assign(detail); if (contentDetail.containerMediaType === 'Tier' && contentDetail.root) { contentDetail.containerMediaType = 'Program'; } if (contentDetail.distanceFrom <= lastDistance) { if (lastDistance !== 100) { currentTree.path = tierSplicer(currentTree.path); trees.push(currentTree); } currentTree = angular.assign({}, contentDetail); workingTree = currentTree; if (currentTree.containerMediaType !== 'Learning Model') { currentTree.treeTitle = currentTree.containerTitle; currentTree.path = [ currentTree.containerMediaType.toLowerCase(), currentTree.parentItemUuid, 0, ].join('/'); } else { currentTree.treeTitle = ''; currentTree.path = ''; } } else { workingTree.parent = angular.assign({}, contentDetail); workingTree = workingTree.parent; if (workingTree.containerMediaType !== 'Learning Model') { if (currentTree.treeTitle === '') { currentTree.treeTitle = workingTree.containerTitle; currentTree.path = [ workingTree.containerMediaType.toLowerCase(), workingTree.parentItemUuid, 0, ].join('/'); } else { currentTree.treeTitle = `${currentTree.treeTitle} : ${workingTree.containerTitle}`; currentTree.path = [ workingTree.containerMediaType.toLowerCase(), workingTree.parentItemUuid, 0, currentTree.path, ].join('/'); } } } lastDistance = contentDetail.distanceFrom; }); if (currentTree) { currentTree.path = tierSplicer(currentTree.path); trees.push(currentTree); } self.fromDetailsTrees = trees; self.fromDetailsForSearch = self.getValidFromDetails(); } propagateAssociativeProps(parent) { const item = this; const associativePropsReady = ContentFactory.$q.defer(); if (ContentFactory.$rootScope.openedItem && ContentFactory.$rootScope.openedItem.id === item.id) { item.associativeProps = ContentFactory.$rootScope.openedItem.associativeProps; associativePropsReady.resolve(); } else if (!parent.id || !parent.version) { associativePropsReady.resolve(); } else { Content.get({ contentId: parent.id, version: parent.version, levels: 1, }, true).then((fullParentData) => { const itemInParentContext = fullParentData.contentItems.find(contentItem => contentItems.id === item.id); item.associativeProps = itemInParentContext ? itemInParentContext.associativeProps : null; associativePropsReady.resolve(); }); } return associativePropsReady.promise; } isScoOrTest() { return this.fileType === 'SCO' || this.fileType === 'TEST' || this.fileType === CONTENT_CONSTANTS.FILE_TYPE.TIN_CAN_SCO; } isScorable() { return this.isScoOrTest(); } isReviewerContainer() { if (this.externalSource) { return this.externalSource.toUpperCase() === 'STANDARD'; } return false; } isLesson() { return this.mediaType === 'Lesson'; } isRRS() { return this.mediaType === MEDIA_TYPE.REALIZE_READER_SELECTION; } hasCustomizedLesson() { return this.isLesson() && this.contribSource === CONTRIBUTOR_SOURCE.PEARSON && this.customizedItemDefaultView; } isTest() { return this.mediaType === 'Test'; } hasCustomizedTest() { return !!(this.isTest() && this.customizedItem && this.hasCustomizedVersion); } isCustomizedTest() { return !!(this.isTest() && this.isCustomizedVersion); } isStudentVoice() { return this.mediaType === 'Student Voice'; } isTestNavTest() { return this.isTest() && this.playerTarget === 'testnav'; } isNativeTest() { return this.isTest() && (this.playerTarget === 'realize' || this.playerTarget === ''); } isDiscussionPrompt() { return this.mediaType === MEDIA_TYPE.DISCUSSION_PROMPT; } getAssignedStatus() { return ContentFactory.api.get('assignmentStatus', { params: { id: this.id } }) .then(response => response.data.replace(/"/g, ''), err => ContentFactory.$q.reject('error getting item assigned status', err)); } isAssigned() { const promise = this.getAssignedStatus().then(status => status === 'ASSIGNED'); return promise; } isCenterProgram() { const self = this; return !!(self.root && self.centersProperties && self.centersProperties.itemUuid !== null); } isGooruItem() { return !!this.gooruOid || isExternalItem(CONTENT_CONSTANTS.FILE_TYPE.GOORU, this); } getExternalResourceDefaultImage() { if (isExternalItem(CONTENT_CONSTANTS.FILE_TYPE.OPEN_ED, this)) { return '/opened_thumbnail_default.png'; } else if (isExternalItem(CONTENT_CONSTANTS.FILE_TYPE.GOORU, this)) { return '/gooru_icon.png'; } return ''; } getLessonItems() { const self = this; const childContents = []; angular.forEach(self.contentItems, (level1) => { if (level1.mediaType !== MEDIA_TYPE.LEARNING_MODEL) { childContents.push(level1); } else { angular.forEach(level1.contentItems, (item) => { const itemClone = Object.assign(itemClone); itemClone.fromLearningModel = true; childContents.push(itemClone); }); } }); return childContents; } getRRSActivities() { let RRSActivities = []; return this.getItemContainer(MEDIA_TYPE.ACTIVITY_SUPPORT) .then((response) => { const responseObj = response.data; if (responseObj && responseObj.contentItems && responseObj.contentItems.length > 0) { RRSActivities = responseObj.contentItems; } this.RRSActivities = RRSActivities; return RRSActivities; }, (error) => { ContentFactory.$log.error('Failed to get activity support container:', error); return error; }); } getItemContainer(type) { return ContentFactory.api.get('itemContainer', { params: { mediaType: type, }, }); } isLtiItem() { return this.mediaType === 'Partner Link'; } static getLtiLaunchInfo(contentId, contentVersion, optionalParams) { const params = optionalParams; params.contentId = contentId; params.contentVersion = contentVersion; return ContentFactory.api.get('externalTool', params).then((response) => { response.data.settings.openNewWindow = angular.fromJson(response.data.settings.openNewWindow); return response.data; }); } getClassroomShareUrl() { const deferred = ContentFactory.$q.defer(); const item = this.getDefaultVersion(); const path = ContentFactory.$location.absUrl(); const target = item.isLesson() ? 'lesson' : 'content'; const contentPath = [path, target, item.id, item.version].join('/'); let details; function prepareShareUrl(ssoUrlDetails) { if (ssoUrlDetails.idpDataUrl) { return `${ssoUrlDetails.rumbaLoginUrl}?service=${contentPath}&idpMetadata=${ssoUrlDetails.idpDataUrl}`; } return `${ssoUrlDetails.rumbaLoginUrl}?service=${contentPath}`; } if (isSSOUrlDetailsAvailable()) { details = ContentFactory.$currentUser.getAttribute('rumba.ssourldetails'); deferred.resolve(prepareShareUrl(details)); } else { ContentFactory.User.getSSOUrlDetails() .then((sslDetails) => { if (sslDetails) { ContentFactory.$currentUser.setAttribute('rumba.ssourldetails', { rumbaLoginUrl: sslDetails.rumbaLoginUrl, idpDataUrl: sslDetails.idpDataUrl, }, false); return deferred.resolve(prepareShareUrl(sslDetails)); } return {}; }, (error) => { deferred.reject(error); }); } return deferred.promise; } isClassroomSharable() { const item = this; const isContainerType = (item.fileType === CONTENT_CONSTANTS.FILE_TYPE.SEQUENCE); if (!ContentFactory.$currentUser.isGoogleClassroomEnabled() || item.teacherOnly === 'Yes' || item.hideFromStudent || (isContainerType && !item.isLesson())) { return false; } return item.mediaType !== MEDIA_TYPE.DISCUSSION_PROMPT && item.mediaType !== MEDIA_TYPE.ADAPTIVE_HOMEWORK && item.mediaType !== MEDIA_TYPE.LEVELED_READER && item.mediaType !== MEDIA_TYPE.PARTNER_LINK && item.mediaType !== MEDIA_TYPE.LEARNING_MODEL && item.mediaType !== MEDIA_TYPE.STUDENT_VOICE && item.mediaType !== MEDIA_TYPE.QUESTION_BANK && item.mediaType !== MEDIA_TYPE.ACTIVITY_SUPPORT; } } function isExternalItem(externalSource, self) { if (!self.fileType) { return false; } const externalSourceLowerCase = angular.lowercase(externalSource); const contributor = angular.lowercase(self.contribSource); const source = angular.lowercase(self.externalSource); return contributor === externalSourceLowerCase || source === externalSourceLowerCase; } function skillsLocaleDescription(skill) { const skillInfo = Object.assign(skill); if ((ContentFactory.$rootScope.currentProgram.language === 'Spanish' || ContentFactory.$rootScope.currentUser.getAttribute('profile.locale') === 'es') && (skillInfo.spanishDescription !== '' && skillInfo.spanishDescription !== null)) { skillInfo.description = skillInfo.spanishDescription; } return skillInfo; } function setSkillsLocaleDescription(response) { const results = []; angular.forEach(response.data.questions, (question) => { question.skillDetails.map(skill => skillsLocaleDescription(skill)); results.push(question); }); return angular.extend({}, response.data, { results, }); } function convertQueryResults(response) { const results = []; angular.forEach(response.data.results, (item) => { results.push(new Content(item)); }); return OpenEdItem.retrieveItemsInList(results, item => item.externalSource === 'OpenEd') .then((allResults) => { const next = angular.assign({}, response.data); next.results = allResults; return next; }); } function updateQueryResultsTeacherOnly(response) { const ids = ContentFactory.arrayUtilities.pluck(response.results.filter(result => result.mediaType === MEDIA_TYPE.Lesson && result.teacherOnly === 'UNKNOWN'), 'id'); let promise; if (ids.length > 0) { promise = Content.getTeacherOnlyValues(ids) .then((teacherOnlyResponse) => { let i; let len; Object.keys(teacherOnlyResponse.data).forEach((key) => { for (i = 0, len = response.results.length; i < len; i += 1) { if (response.results[i].id === key) { response.results[i].teacherOnly = teacherOnlyResponse.data[key] ? 'Yes' : 'No'; break; } } }); const next = angular.assign({}, response); return next; }); return promise; } ContentFactory.$log.log('content.query : no unknowns to test'); const next = angular.assign({}, response); return next; } function getJsonKeyForAssociatedItem(name) { switch (name) { case 'Teacher Support': return 'associatedTeacherSupport'; case 'Tools': return 'associatedTools'; default: return ''; } } function tierSplicer(path) { const r = /tier/g; let match = null; let idx = 0; let count = 0; let result = path; match = r.exec(path); while (match) { count += 1; if (count > 1) { idx = match.index + 4 + (count - 2); result = result.slice(0, idx) + count + result.slice(idx); } match = r.exec(path); } return result; } function isSSOUrlDetailsAvailable() { return ContentFactory.$currentUser.getAttribute('rumba.ssourldetails'); } export function ContentFactory($filter, browserInfo, Paths, $log, $q, $cacheFactory, mediaQuery, objectUtilities, $rootScope, arrayUtilities, $currentUser, $location, User, api) { ContentFactory.$filter = $filter; ContentFactory.browserInfo = browserInfo; ContentFactory.Paths = Paths; ContentFactory.$log = $log; ContentFactory.$q = $q; ContentFactory.$cacheFactory = $cacheFactory; ContentFactory.MediaQuery = mediaQuery; ContentFactory.ObjectUtilities = objectUtilities; ContentFactory.$rootScope = $rootScope; ContentFactory.arrayUtilities = arrayUtilities; ContentFactory.$currentUser = $currentUser; ContentFactory.$location = $location; ContentFactory.User = User; ContentFactory.api = api; return Content; } export default ContentFactory;