MOBILE-2342 glossary: Implement glossary and offline providers
parent
d241e1d253
commit
5106e11e29
|
@ -0,0 +1,30 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { AddonModGlossaryProvider } from './providers/glossary';
|
||||
import { AddonModGlossaryOfflineProvider } from './providers/offline';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
],
|
||||
providers: [
|
||||
AddonModGlossaryProvider,
|
||||
AddonModGlossaryOfflineProvider,
|
||||
]
|
||||
})
|
||||
export class AddonModGlossaryModule {
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"addentry": "Add a new entry",
|
||||
"aliases": "Keyword(s)",
|
||||
"attachment": "Attachment",
|
||||
"browsemode": "Browse entries",
|
||||
"byalphabet": "Alphabetically",
|
||||
"byauthor": "Group by author",
|
||||
"bycategory": "Group by category",
|
||||
"bynewestfirst": "Newest first",
|
||||
"byrecentlyupdated": "Recently updated",
|
||||
"bysearch": "Search",
|
||||
"cannoteditentry": "Cannot edit entry",
|
||||
"casesensitive": "This entry is case sensitive",
|
||||
"categories": "Categories",
|
||||
"concept": "Concept",
|
||||
"definition": "Definition",
|
||||
"entriestobesynced": "Entries to be synced",
|
||||
"entrypendingapproval": "This entry is pending approval.",
|
||||
"entryusedynalink": "This entry should be automatically linked",
|
||||
"errconceptalreadyexists": "This concept already exists. No duplicates allowed in this glossary.",
|
||||
"errorloadingentries": "An error occurred while loading entries.",
|
||||
"errorloadingentry": "An error occurred while loading the entry.",
|
||||
"errorloadingglossary": "An error occurred while loading the glossary.",
|
||||
"fillfields": "Concept and definition are mandatory fields.",
|
||||
"fullmatch": "Match whole words only",
|
||||
"linking": "Auto-linking",
|
||||
"noentriesfound": "No entries were found.",
|
||||
"searchquery": "Search query"
|
||||
}
|
|
@ -0,0 +1,886 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreSite } from '@classes/site';
|
||||
import { CoreAppProvider } from '@providers/app';
|
||||
import { CoreFilepoolProvider } from '@providers/filepool';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { AddonModGlossaryOfflineProvider } from './offline';
|
||||
|
||||
/**
|
||||
* Service that provides some features for glossaries.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonModGlossaryProvider {
|
||||
static COMPONENT = 'mmaModGlossary';
|
||||
static LIMIT_ENTRIES = 25;
|
||||
static LIMIT_CATEGORIES = 10;
|
||||
static SHOW_ALL_CATERGORIES = 0;
|
||||
static SHOW_NOT_CATEGORISED = -1;
|
||||
|
||||
static ADD_ENTRY_EVENT = 'addon_mod_glossary_add_entry';
|
||||
|
||||
protected ROOT_CACHE_KEY = 'mmaModGlossary:';
|
||||
|
||||
constructor(private appProvider: CoreAppProvider,
|
||||
private sitesProvider: CoreSitesProvider,
|
||||
private filepoolProvider: CoreFilepoolProvider,
|
||||
private translate: TranslateService,
|
||||
private textUtils: CoreTextUtilsProvider,
|
||||
private utils: CoreUtilsProvider,
|
||||
private glossaryOffline: AddonModGlossaryOfflineProvider) {}
|
||||
|
||||
/**
|
||||
* Get the course glossary cache key.
|
||||
*
|
||||
* @param {number} courseId Course Id.
|
||||
* @return {string} Cache key.
|
||||
*/
|
||||
protected getCourseGlossariesCacheKey(courseId: number): string {
|
||||
return this.ROOT_CACHE_KEY + 'courseGlossaries:' + courseId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the glossaries in a course.
|
||||
*
|
||||
* @param {number} courseId Course Id.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any[]>} Resolved with the glossaries.
|
||||
*/
|
||||
getCourseGlossaries(courseId: number, siteId?: string): Promise<any[]> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const params = {
|
||||
courseids: [courseId]
|
||||
};
|
||||
const preSets = {
|
||||
cacheKey: this.getCourseGlossariesCacheKey(courseId)
|
||||
};
|
||||
|
||||
return site.read('mod_glossary_get_glossaries_by_courses', params, preSets).then((result) => {
|
||||
return result.glossaries;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate all glossaries in a course.
|
||||
*
|
||||
* @param {number} courseId Course Id.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any[]>} Resolved when data is invalidated.
|
||||
*/
|
||||
invalidateCourseGlossaries(courseId: number, siteId?: string): Promise<any[]> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const key = this.getCourseGlossariesCacheKey(courseId);
|
||||
|
||||
return site.invalidateWsCacheForKey(key);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entries by author cache key.
|
||||
*
|
||||
* @param {number} glossaryId Glossary Id.
|
||||
* @param {string} letter First letter of firstname or lastname, or either keywords: ALL or SPECIAL.
|
||||
* @param {string} field Search and order using: FIRSTNAME or LASTNAME
|
||||
* @param {string} sort The direction of the order: ASC or DESC
|
||||
* @return {string} Cache key.
|
||||
*/
|
||||
protected getEntriesByAuthorCacheKey(glossaryId: number, letter: string, field: string, sort: string): string {
|
||||
return this.ROOT_CACHE_KEY + 'entriesByAuthor:' + glossaryId + ':' + letter + ':' + field + ':' + sort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get entries by author.
|
||||
*
|
||||
* @param {number} glossaryId Glossary Id.
|
||||
* @param {string} letter First letter of firstname or lastname, or either keywords: ALL or SPECIAL.
|
||||
* @param {string} field Search and order using: FIRSTNAME or LASTNAME
|
||||
* @param {string} sort The direction of the order: ASC or DESC
|
||||
* @param {number} from Start returning records from here.
|
||||
* @param {number} limit Number of records to return.
|
||||
* @param {boolean} forceCache True to always get the value from cache, false otherwise. Default false.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any[]>} Resolved with the entries.
|
||||
*/
|
||||
getEntriesByAuthor(glossaryId: number, letter: string, field: string, sort: string, from: number, limit: number,
|
||||
forceCache: boolean, siteId?: string): Promise<any[]> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const params = {
|
||||
id: glossaryId,
|
||||
letter: letter,
|
||||
field: field,
|
||||
sort: sort,
|
||||
from: from,
|
||||
limit: limit
|
||||
};
|
||||
const preSets = {
|
||||
cacheKey: this.getEntriesByAuthorCacheKey(glossaryId, letter, field, sort),
|
||||
omitExpires: forceCache
|
||||
};
|
||||
|
||||
return site.read('mod_glossary_get_entries_by_author', params, preSets);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate cache of entries by author.
|
||||
*
|
||||
* @param {number} glossaryId Glossary Id.
|
||||
* @param {string} letter First letter of firstname or lastname, or either keywords: ALL or SPECIAL.
|
||||
* @param {string} field Search and order using: FIRSTNAME or LASTNAME
|
||||
* @param {string} sort The direction of the order: ASC or DESC
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Resolved when data is invalidated.
|
||||
*/
|
||||
invalidateEntriesByAuthor(glossaryId: number, letter: string, field: string, sort: string, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const key = this.getEntriesByAuthorCacheKey(glossaryId, letter, field, sort);
|
||||
|
||||
return site.invalidateWsCacheForKey(key);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get entries by category.
|
||||
*
|
||||
* @param {number} glossaryId Glossary Id.
|
||||
* @param {string} categoryId The category ID. Use constant SHOW_ALL_CATERGORIES for all categories, or
|
||||
* constant SHOW_NOT_CATEGORISED for uncategorised entries.
|
||||
* @param {number} from Start returning records from here.
|
||||
* @param {number} limit Number of records to return.
|
||||
* @param {boolean} forceCache True to always get the value from cache, false otherwise. Default false.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any[]>} Resolved with the entries.
|
||||
*/
|
||||
getEntriesByCategory(glossaryId: number, categoryId: number, from: number, limit: number, forceCache: boolean,
|
||||
siteId?: string): Promise<any[]> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const params = {
|
||||
id: glossaryId,
|
||||
categoryid: categoryId,
|
||||
from: from,
|
||||
limit: limit
|
||||
};
|
||||
const preSets = {
|
||||
cacheKey: this.getEntriesByCategoryCacheKey(glossaryId, categoryId),
|
||||
omitExpires: forceCache
|
||||
};
|
||||
|
||||
return site.read('mod_glossary_get_entries_by_category', params, preSets);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate cache of entries by category.
|
||||
*
|
||||
* @param {number} glossaryId Glossary Id.
|
||||
* @param {string} categoryId The category ID. Use constant SHOW_ALL_CATERGORIES for all categories, or
|
||||
* constant SHOW_NOT_CATEGORISED for uncategorised entries.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Resolved when data is invalidated.
|
||||
*/
|
||||
invalidateEntriesByCategory(glossaryId: number, categoryId: number, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const key = this.getEntriesByCategoryCacheKey(glossaryId, categoryId);
|
||||
|
||||
return site.invalidateWsCacheForKey(key);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entries by category cache key.
|
||||
*
|
||||
* @param {number} glossaryId Glossary Id.
|
||||
* @param {string} categoryId The category ID. Use constant SHOW_ALL_CATERGORIES for all categories, or
|
||||
* constant SHOW_NOT_CATEGORISED for uncategorised entries.
|
||||
* @return {string} Cache key.
|
||||
*/
|
||||
getEntriesByCategoryCacheKey(glossaryId: number, categoryId: number): string {
|
||||
return this.ROOT_CACHE_KEY + 'entriesByCategory:' + glossaryId + ':' + categoryId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entries by date cache key.
|
||||
*
|
||||
* @param {number} glossaryId Glossary Id.
|
||||
* @param {string} order The way to order the records.
|
||||
* @param {string} sort The direction of the order.
|
||||
* @return {string} Cache key.
|
||||
*/
|
||||
getEntriesByDateCacheKey(glossaryId: number, order: string, sort: string): string {
|
||||
return this.ROOT_CACHE_KEY + 'entriesByDate:' + glossaryId + ':' + order + ':' + sort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get entries by date.
|
||||
*
|
||||
* @param {number} glossaryId Glossary Id.
|
||||
* @param {string} order The way to order the records.
|
||||
* @param {string} sort The direction of the order.
|
||||
* @param {number} from Start returning records from here.
|
||||
* @param {number} limit Number of records to return.
|
||||
* @param {boolean} forceCache True to always get the value from cache, false otherwise. Default false.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any[]>} Resolved with the entries.
|
||||
*/
|
||||
getEntriesByDate(glossaryId: number, order: string, sort: string, from: number, limit: number, forceCache: boolean,
|
||||
siteId?: string): Promise<any[]> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const params = {
|
||||
id: glossaryId,
|
||||
order: order,
|
||||
sort: sort,
|
||||
from: from,
|
||||
limit: limit
|
||||
};
|
||||
const preSets = {
|
||||
cacheKey: this.getEntriesByDateCacheKey(glossaryId, order, sort),
|
||||
omitExpires: forceCache
|
||||
};
|
||||
|
||||
return site.read('mod_glossary_get_entries_by_date', params, preSets);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate cache of entries by date.
|
||||
*
|
||||
* @param {number} glossaryId Glossary Id.
|
||||
* @param {string} order The way to order the records.
|
||||
* @param {string} sort The direction of the order.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Resolved when data is invalidated.
|
||||
*/
|
||||
invalidateEntriesByDate(glossaryId: number, order: string, sort: string, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const key = this.getEntriesByDateCacheKey(glossaryId, order, sort);
|
||||
|
||||
return site.invalidateWsCacheForKey(key);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entries by letter cache key.
|
||||
*
|
||||
* @param {number} glossaryId Glossary Id.
|
||||
* @param {string} letter A letter, or a special keyword.
|
||||
* @return {string} Cache key.
|
||||
*/
|
||||
protected getEntriesByLetterCacheKey(glossaryId: number, letter: string): string {
|
||||
return this.ROOT_CACHE_KEY + 'entriesByLetter:' + glossaryId + ':' + letter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get entries by letter.
|
||||
*
|
||||
* @param {number} glossaryId Glossary Id.
|
||||
* @param {string} letter A letter, or a special keyword.
|
||||
* @param {number} from Start returning records from here.
|
||||
* @param {number} limit Number of records to return.
|
||||
* @param {boolean} forceCache True to always get the value from cache, false otherwise. Default false.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any[]>} Resolved with the entries.
|
||||
*/
|
||||
getEntriesByLetter(glossaryId: number, letter: string, from: number, limit: number, forceCache: boolean, siteId?: string):
|
||||
Promise<any[]> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const params = {
|
||||
id: glossaryId,
|
||||
letter: letter,
|
||||
from: from,
|
||||
limit: limit
|
||||
};
|
||||
const preSets = {
|
||||
cacheKey: this.getEntriesByLetterCacheKey(glossaryId, letter),
|
||||
omitExpires: forceCache
|
||||
};
|
||||
|
||||
return site.read('mod_glossary_get_entries_by_letter', params, preSets);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate cache of entries by letter.
|
||||
*
|
||||
* @param {number} glossaryId Glossary Id.
|
||||
* @param {string} letter A letter, or a special keyword.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Resolved when data is invalidated.
|
||||
*/
|
||||
invalidateEntriesByLetter(glossaryId: number, letter: string, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const key = this.getEntriesByLetterCacheKey(glossaryId, letter);
|
||||
|
||||
return site.invalidateWsCacheForKey(key);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entries by search cache key.
|
||||
*
|
||||
* @param {number} glossaryId Glossary Id.
|
||||
* @param {string} query The search query.
|
||||
* @param {boolean} fullSearch Whether or not full search is required.
|
||||
* @param {string} order The way to order the results.
|
||||
* @param {string} sort The direction of the order.
|
||||
* @return {string} Cache key.
|
||||
*/
|
||||
protected getEntriesBySearchCacheKey(glossaryId: number, query: string, fullSearch: boolean, order: string, sort: string):
|
||||
string {
|
||||
return this.ROOT_CACHE_KEY + 'entriesBySearch:' + glossaryId + ':' + fullSearch + ':' + order + ':' + sort + ':' + query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get entries by search.
|
||||
*
|
||||
* @param {number} glossaryId Glossary Id.
|
||||
* @param {string} query The search query.
|
||||
* @param {boolean} fullSearch Whether or not full search is required.
|
||||
* @param {string} order The way to order the results.
|
||||
* @param {string} sort The direction of the order.
|
||||
* @param {number} from Start returning records from here.
|
||||
* @param {number} limit Number of records to return.
|
||||
* @param {boolean} forceCache True to always get the value from cache, false otherwise. Default false.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any[]>} Resolved with the entries.
|
||||
*/
|
||||
getEntriesBySearch(glossaryId: number, query: string, fullSearch: boolean, order: string, sort: string, from: number,
|
||||
limit: number, forceCache: boolean, siteId?: string): Promise<any[]> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const params = {
|
||||
id: glossaryId,
|
||||
query: query,
|
||||
fullsearch: fullSearch,
|
||||
order: order,
|
||||
sort: sort,
|
||||
from: from,
|
||||
limit: limit
|
||||
};
|
||||
const preSets = {
|
||||
cacheKey: this.getEntriesBySearchCacheKey(glossaryId, query, fullSearch, order, sort),
|
||||
omitExpires: forceCache,
|
||||
};
|
||||
|
||||
return site.read('mod_glossary_get_entries_by_search', params, preSets);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate cache of entries by search.
|
||||
*
|
||||
* @param {number} glossaryId Glossary Id.
|
||||
* @param {string} query The search query.
|
||||
* @param {boolean} fullSearch Whether or not full search is required.
|
||||
* @param {string} order The way to order the results.
|
||||
* @param {string} sort The direction of the order.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Resolved when data is invalidated.
|
||||
*/
|
||||
invalidateEntriesBySearch(glossaryId: number, query: string, fullSearch: boolean, order: string, sort: string, siteId?: string):
|
||||
Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const key = this.getEntriesBySearchCacheKey(glossaryId, query, fullSearch, order, sort);
|
||||
|
||||
return site.invalidateWsCacheForKey(key);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the glossary categories cache key.
|
||||
*
|
||||
* @param {number} glossaryId Glossary Id.
|
||||
* @return {string} The cache key.
|
||||
*/
|
||||
protected getCategoriesCacheKey(glossaryId: number): string {
|
||||
return this.ROOT_CACHE_KEY + 'categories:' + glossaryId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the categories related to the glossary.
|
||||
*
|
||||
* @param {number} glossaryId Glossary Id.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any[]>} Promise resolved with the categories if supported or empty array if not.
|
||||
*/
|
||||
getAllCategories(glossaryId: number, siteId?: string): Promise<any[]> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return this.getCategories(glossaryId, 0, AddonModGlossaryProvider.LIMIT_CATEGORIES, [], site);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the categories related to the glossary by sections. It's a recursive function see initial call values.
|
||||
*
|
||||
* @param {number} glossaryId Glossary Id.
|
||||
* @param {number} from Number of categories already fetched, so fetch will be done from this number. Initial value 0.
|
||||
* @param {number} limit Number of categories to fetch. Initial value LIMIT_CATEGORIES.
|
||||
* @param {any[]} categories Already fetched categories where to append the fetch. Initial value [].
|
||||
* @param {any} site Site object.
|
||||
* @return {Promise<any[]>} Promise resolved with the categories.
|
||||
*/
|
||||
protected getCategories(glossaryId: number, from: number, limit: number, categories: any[], site: CoreSite): Promise<any[]> {
|
||||
const params = {
|
||||
id: glossaryId,
|
||||
from: from,
|
||||
limit: limit
|
||||
};
|
||||
const preSets = {
|
||||
cacheKey: this.getCategoriesCacheKey(glossaryId)
|
||||
};
|
||||
|
||||
return site.read('mod_glossary_get_categories', params, preSets).then((response) => {
|
||||
categories = categories.concat(response.categories);
|
||||
const canLoadMore = (from + limit) < response.count;
|
||||
if (canLoadMore) {
|
||||
from += limit;
|
||||
|
||||
return this.getCategories(glossaryId, from, limit, categories, site);
|
||||
}
|
||||
|
||||
return categories;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate cache of categories by glossary id.
|
||||
*
|
||||
* @param {number} glossaryId Glossary Id.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved when categories data has been invalidated,
|
||||
*/
|
||||
invalidateCategories(glossaryId: number, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.invalidateWsCacheForKey(this.getCategoriesCacheKey(glossaryId));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an entry by ID cache key.
|
||||
*
|
||||
* @param {number} entryId Entry Id.
|
||||
* @return {string} Cache key.
|
||||
*/
|
||||
protected getEntryCacheKey(entryId: number): string {
|
||||
return this.ROOT_CACHE_KEY + 'getEntry:' + entryId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get one entry by ID.
|
||||
*
|
||||
* @param {number} entryId Entry ID.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved with the entry.
|
||||
*/
|
||||
getEntry(entryId: number, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const params = {
|
||||
id: entryId
|
||||
};
|
||||
const preSets = {
|
||||
cacheKey: this.getEntryCacheKey(entryId)
|
||||
};
|
||||
|
||||
return site.read('mod_glossary_get_entry_by_id', params, preSets).then((response) => {
|
||||
if (response && response.entry) {
|
||||
return response.entry;
|
||||
} else {
|
||||
return Promise.reject(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the fetch of the entries using the propper function and arguments.
|
||||
*
|
||||
* @param {Function} fetchFunction Function to fetch.
|
||||
* @param {any[]} fetchArguments Arguments to call the fetching.
|
||||
* @param {number} [limitFrom=0] Number of entries already fetched, so fetch will be done from this number.
|
||||
* @param {number} [limitNum] Number of records to return. Defaults to LIMIT_ENTRIES.
|
||||
* @param {boolean} [forceCache=false] True to always get the value from cache, false otherwise. Default false.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved with the response.
|
||||
*/
|
||||
fetchEntries(fetchFunction: Function, fetchArguments: any[], limitFrom: number = 0, limitNum?: number,
|
||||
forceCache: boolean = false, siteId?: string): Promise<any> {
|
||||
limitNum = limitNum || AddonModGlossaryProvider.LIMIT_ENTRIES;
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
const args = fetchArguments.slice();
|
||||
args.push(limitFrom);
|
||||
args.push(limitNum);
|
||||
args.push(forceCache);
|
||||
args.push(siteId);
|
||||
|
||||
return fetchFunction.apply(this, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the whole fetch of the entries using the propper function and arguments.
|
||||
*
|
||||
* @param {Function} fetchFunction Function to fetch.
|
||||
* @param {any[]} fetchArguments Arguments to call the fetching.
|
||||
* @param {boolean} [forceCache=false] True to always get the value from cache, false otherwise. Default false.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any[]>} Promise resolved with all entrries.
|
||||
*/
|
||||
fetchAllEntries(fetchFunction: Function, fetchArguments: any[], forceCache: boolean = false, siteId?: string): Promise<any[]> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
const entries = [];
|
||||
const limitNum = AddonModGlossaryProvider.LIMIT_ENTRIES;
|
||||
|
||||
const fetchMoreEntries = (): Promise<any[]> => {
|
||||
return this.fetchEntries(fetchFunction, fetchArguments, entries.length, limitNum, forceCache, siteId).then((result) => {
|
||||
Array.prototype.push.apply(entries, result.entries);
|
||||
|
||||
return entries.length < result.count ? fetchMoreEntries() : entries;
|
||||
});
|
||||
};
|
||||
|
||||
return fetchMoreEntries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate cache of entry by ID.
|
||||
*
|
||||
* @param {number} entryId Entry Id.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Resolved when data is invalidated.
|
||||
*/
|
||||
invalidateEntry(entryId: number, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.invalidateWsCacheForKey(this.getEntryCacheKey(entryId));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate cache of all entries in the array.
|
||||
*
|
||||
* @param {any[]} entries Entry objects to invalidate.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Resolved when data is invalidated.
|
||||
*/
|
||||
protected invalidateEntries(entries: any[], siteId?: string): Promise<any> {
|
||||
const keys = [];
|
||||
entries.forEach((entry) => {
|
||||
keys.push(this.getEntryCacheKey(entry.id));
|
||||
});
|
||||
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.invalidateMultipleWsCacheForKey(keys);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate the prefetched content except files.
|
||||
* To invalidate files, use AddonModGlossary#invalidateFiles.
|
||||
*
|
||||
* @param {number} moduleId The module ID.
|
||||
* @param {number} courseId Course ID.
|
||||
* @return {Promise<any>} Promise resolved when data is invalidated.
|
||||
*/
|
||||
invalidateContent(moduleId: number, courseId: number): Promise<any> {
|
||||
return this.getGlossary(courseId, moduleId).then((glossary) => {
|
||||
return this.invalidateGlossaryEntries(glossary).finally(() => {
|
||||
return this.utils.allPromises([
|
||||
this.invalidateCourseGlossaries(courseId),
|
||||
this.invalidateCategories(glossary.id)
|
||||
]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate the prefetched content for a given glossary, except files.
|
||||
* To invalidate files, use $mmaModGlossary#invalidateFiles.
|
||||
*
|
||||
* @param {any} glossary The glossary object.
|
||||
* @param {boolean} [onlyEntriesList] If true, entries won't be invalidated.
|
||||
* @return {Promise<any>} Promise resolved when data is invalidated.
|
||||
*/
|
||||
invalidateGlossaryEntries(glossary: any, onlyEntriesList?: boolean): Promise<any> {
|
||||
const promises = [];
|
||||
|
||||
if (!onlyEntriesList) {
|
||||
promises.push(this.fetchAllEntries(this.getEntriesByLetter, [glossary.id, 'ALL'], true).then((entries) => {
|
||||
return this.invalidateEntries(entries);
|
||||
}));
|
||||
}
|
||||
|
||||
glossary.browsemodes.forEach((mode) => {
|
||||
switch (mode) {
|
||||
case 'letter':
|
||||
promises.push(this.invalidateEntriesByLetter(glossary.id, 'ALL'));
|
||||
break;
|
||||
case 'cat':
|
||||
promises.push(this.invalidateEntriesByCategory(glossary.id, AddonModGlossaryProvider.SHOW_ALL_CATERGORIES));
|
||||
break;
|
||||
case 'date':
|
||||
promises.push(this.invalidateEntriesByDate(glossary.id, 'CREATION', 'DESC'));
|
||||
promises.push(this.invalidateEntriesByDate(glossary.id, 'UPDATE', 'DESC'));
|
||||
break;
|
||||
case 'author':
|
||||
promises.push(this.invalidateEntriesByAuthor(glossary.id, 'ALL', 'LASTNAME', 'ASC'));
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
|
||||
return this.utils.allPromises(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate the prefetched files.
|
||||
*
|
||||
* @param {number} moduleId The module ID.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved when the files are invalidated.
|
||||
*/
|
||||
protected invalidateFiles(moduleId: number, siteId?: string): Promise<any> {
|
||||
return this.filepoolProvider.invalidateFilesByComponent(siteId, AddonModGlossaryProvider.COMPONENT, moduleId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get one glossary by cmid.
|
||||
*
|
||||
* @param {number} courseId Course Id.
|
||||
* @param {number} cmId Course Module Id.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved with the glossary.
|
||||
*/
|
||||
getGlossary(courseId: number, cmId: number, siteId?: string): Promise<any> {
|
||||
return this.getCourseGlossaries(courseId, siteId).then((glossaries) => {
|
||||
const glossary = glossaries.find((glossary) => glossary.coursemodule == cmId);
|
||||
|
||||
if (glossary) {
|
||||
return glossary;
|
||||
}
|
||||
|
||||
return Promise.reject(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get one glossary by glossary ID.
|
||||
*
|
||||
* @param {number} courseId Course Id.
|
||||
* @param {number} glossaryId Glossary Id.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved with the glossary.
|
||||
*/
|
||||
getGlossaryById(courseId: number, glossaryId: number, siteId?: string): Promise<any> {
|
||||
return this.getCourseGlossaries(courseId, siteId).then((glossaries) => {
|
||||
const glossary = glossaries.find((glossary) => glossary.id == glossaryId);
|
||||
|
||||
if (glossary) {
|
||||
return glossary;
|
||||
}
|
||||
|
||||
return Promise.reject(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new entry on a glossary
|
||||
*
|
||||
* @param {number} glossaryId Glossary ID.
|
||||
* @param {string} concept Glossary entry concept.
|
||||
* @param {string} definition Glossary entry concept definition.
|
||||
* @param {number} courseId Course ID of the glossary.
|
||||
* @param {any} [options] Array of options for the entry.
|
||||
* @param {any} [attach] Attachments ID if sending online, result of $mmFileUploader#storeFilesToUpload otherwise.
|
||||
* @param {number} [timeCreated] The time the entry was created. If not defined, current time.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @param {any} [discardEntry] The entry provided will be discarded if found.
|
||||
* @param {boolean} [allowOffline] True if it can be stored in offline, false otherwise.
|
||||
* @param {boolean} [checkDuplicates] Check for duplicates before storing offline. Only used if allowOffline is true.
|
||||
* @return {Promise<number | false>} Promise resolved with entry ID if entry was created in server, false if stored in device.
|
||||
*/
|
||||
addEntry(glossaryId: number, concept: string, definition: string, courseId: number, options: any, attach: any,
|
||||
timeCreated: number, siteId?: string, discardEntry?: any, allowOffline?: boolean, checkDuplicates?: boolean):
|
||||
Promise<number | false> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
// Convenience function to store a new entry to be synchronized later.
|
||||
const storeOffline = (): Promise<number | false> => {
|
||||
const discardTime = discardEntry && discardEntry.timecreated;
|
||||
|
||||
let duplicatesPromise;
|
||||
if (checkDuplicates) {
|
||||
duplicatesPromise = this.isConceptUsed(glossaryId, concept, discardTime, siteId);
|
||||
} else {
|
||||
duplicatesPromise = Promise.resolve(false);
|
||||
}
|
||||
|
||||
// Check if the entry is duplicated in online or offline mode.
|
||||
return duplicatesPromise.then((used) => {
|
||||
if (used) {
|
||||
return Promise.reject(this.translate.instant('addon.mod_glossary.errconceptalreadyexists'));
|
||||
}
|
||||
|
||||
return this.glossaryOffline.addNewEntry(glossaryId, concept, definition, courseId, attach, options, timeCreated,
|
||||
siteId, undefined, discardEntry).then(() => {
|
||||
return false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (!this.appProvider.isOnline() && allowOffline) {
|
||||
// App is offline, store the action.
|
||||
return storeOffline();
|
||||
}
|
||||
|
||||
// If we are editing an offline entry, discard previous first.
|
||||
let discardPromise;
|
||||
if (discardEntry) {
|
||||
discardPromise = this.glossaryOffline.deleteNewEntry(
|
||||
glossaryId, discardEntry.concept, discardEntry.timecreated, siteId);
|
||||
} else {
|
||||
discardPromise = Promise.resolve();
|
||||
}
|
||||
|
||||
return discardPromise.then(() => {
|
||||
// Try to add it in online.
|
||||
return this.addEntryOnline(glossaryId, concept, definition, options, attach, siteId).then((entryId) => {
|
||||
return entryId;
|
||||
}).catch((error) => {
|
||||
if (allowOffline && !this.utils.isWebServiceError(error)) {
|
||||
// Couldn't connect to server, store in offline.
|
||||
return storeOffline();
|
||||
} else {
|
||||
// The WebService has thrown an error or offline not supported, reject.
|
||||
return Promise.reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new entry on a glossary. It does not cache calls. It will fail if offline or cannot connect.
|
||||
*
|
||||
* @param {number} glossaryId Glossary ID.
|
||||
* @param {string} concept Glossary entry concept.
|
||||
* @param {string} definition Glossary entry concept definition.
|
||||
* @param {any} [options] Array of options for the entry.
|
||||
* @param {number} [attachId] Attachments ID (if any attachment).
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<number>} Promise resolved with the entry ID if created, rejected otherwise.
|
||||
*/
|
||||
addEntryOnline(glossaryId: number, concept: string, definition: string, options?: any, attachId?: number, siteId?: string):
|
||||
Promise<number> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const params = {
|
||||
glossaryid: glossaryId,
|
||||
concept: concept,
|
||||
definition: definition,
|
||||
definitionformat: 1,
|
||||
options: this.utils.objectToArrayOfObjects(options || {}, 'name', 'value')
|
||||
};
|
||||
|
||||
if (attachId) {
|
||||
params.options.push({
|
||||
name: 'attachmentsid',
|
||||
value: attachId
|
||||
});
|
||||
}
|
||||
|
||||
// Workaround for bug MDL-57737.
|
||||
if (!site.isVersionGreaterEqualThan('3.2.2')) {
|
||||
params.definition = this.textUtils.cleanTags(params.definition);
|
||||
}
|
||||
|
||||
return site.write('mod_glossary_add_entry', params).then((response) => {
|
||||
if (response && response.entryid) {
|
||||
return response.entryid;
|
||||
}
|
||||
|
||||
return this.utils.createFakeWSError('');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a entry concept is already used.
|
||||
*
|
||||
* @param {number} glossaryId Glossary ID.
|
||||
* @param {string} concept Concept to check.
|
||||
* @param {number} [timeCreated] Timecreated to check that is not the timecreated we are editing.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<boolean>} Promise resolved with true if used, resolved with false if not used or error.
|
||||
*/
|
||||
isConceptUsed(glossaryId: number, concept: string, timeCreated?: number, siteId?: string): Promise<boolean> {
|
||||
// Check offline first.
|
||||
return this.glossaryOffline.isConceptUsed(glossaryId, concept, timeCreated, siteId).then((exists) => {
|
||||
if (exists) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we get here, there's no offline entry with this name, check online.
|
||||
// Get entries from the cache.
|
||||
return this.fetchAllEntries(this.getEntriesByLetter, [glossaryId, 'ALL'], true, siteId).then((entries) => {
|
||||
// Check if there's any entry with the same concept.
|
||||
return entries.some((entry) => entry.concept == concept);
|
||||
});
|
||||
}).catch(() => {
|
||||
// Error, assume not used.
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether or not the plugin is enabled for editing in the current site. Plugin is enabled if the glossary WS are
|
||||
* available.
|
||||
*
|
||||
* @return {boolean} Whether the glossary editing is available or not.
|
||||
*/
|
||||
isPluginEnabledForEditing(): boolean {
|
||||
return this.sitesProvider.getCurrentSite().wsAvailable('mod_glossary_add_entry');
|
||||
}
|
||||
|
||||
/**
|
||||
* Report a glossary as being viewed.
|
||||
*
|
||||
* @param {number} glossaryId Glossary ID.
|
||||
* @param {string} mode The mode in which the glossary was viewed.
|
||||
* @return {Promise<any>} Promise resolved when the WS call is successful.
|
||||
*/
|
||||
logView(glossaryId: number, mode: string): Promise<any> {
|
||||
const params = {
|
||||
id: glossaryId,
|
||||
mode: mode
|
||||
};
|
||||
|
||||
return this.sitesProvider.getCurrentSite().write('mod_glossary_view_glossary', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Report a glossary entry as being viewed.
|
||||
*
|
||||
* @param {number} entryId Entry ID.
|
||||
* @return {Promise<any>} Promise resolved when the WS call is successful.
|
||||
*/
|
||||
logEntryView(entryId: number): Promise<any> {
|
||||
const params = {
|
||||
id: entryId
|
||||
};
|
||||
|
||||
return this.sitesProvider.getCurrentSite().write('mod_glossary_view_entry', params);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CoreFileProvider } from '@providers/file';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
|
||||
/**
|
||||
* Service to handle offline glossary.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonModGlossaryOfflineProvider {
|
||||
|
||||
// Variables for database.
|
||||
protected ENTRIES_TABLE = 'addon_mod_glossary_entrues';
|
||||
|
||||
protected tablesSchema = [
|
||||
{
|
||||
name: this.ENTRIES_TABLE,
|
||||
columns: [
|
||||
{
|
||||
name: 'glossaryid',
|
||||
type: 'INTEGER',
|
||||
},
|
||||
{
|
||||
name: 'courseid',
|
||||
type: 'INTEGER',
|
||||
},
|
||||
{
|
||||
name: 'concept',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
name: 'definition',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
name: 'definitionformat',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
name: 'userid',
|
||||
type: 'INTEGER',
|
||||
},
|
||||
{
|
||||
name: 'timecreated',
|
||||
type: 'INTEGER',
|
||||
},
|
||||
{
|
||||
name: 'options',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
name: 'attachments',
|
||||
type: 'TEXT',
|
||||
},
|
||||
],
|
||||
primaryKeys: ['glossaryid', 'concept', 'timecreated']
|
||||
}
|
||||
];
|
||||
|
||||
constructor(private fileProvider: CoreFileProvider,
|
||||
private sitesProvider: CoreSitesProvider,
|
||||
private textUtils: CoreTextUtilsProvider,
|
||||
private utils: CoreUtilsProvider) {
|
||||
this.sitesProvider.createTablesFromSchema(this.tablesSchema);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a new entry.
|
||||
*
|
||||
* @param {number} glossaryId Glossary ID.
|
||||
* @param {string} concept Glossary entry concept.
|
||||
* @param {number} timeCreated The time the entry was created.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<void>} Promise resolved if deleted, rejected if failure.
|
||||
*/
|
||||
deleteNewEntry(glossaryId: number, concept: string, timeCreated: number, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const conditions = {
|
||||
glossaryid: glossaryId,
|
||||
concept: concept,
|
||||
timecreated: timeCreated,
|
||||
};
|
||||
|
||||
return site.getDb().deleteRecords(this.ENTRIES_TABLE, conditions);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the stored new entries from all the glossaries.
|
||||
*
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any[]>} Promise resolved with entries.
|
||||
*/
|
||||
getAllNewEntries(siteId?: string): Promise<any[]> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.getDb().getRecords(this.ENTRIES_TABLE).then((records: any[]) => {
|
||||
return records.map(this.parseRecord.bind(this));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a stored new entry.
|
||||
*
|
||||
* @param {number} glossaryId Glossary ID.
|
||||
* @param {string} concept Glossary entry concept.
|
||||
* @param {number} timeCreated The time the entry was created.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved with entry.
|
||||
*/
|
||||
getNewEntry(glossaryId: number, concept: string, timeCreated: number, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const conditions = {
|
||||
glossaryid: glossaryId,
|
||||
concept: concept,
|
||||
timecreated: timeCreated,
|
||||
};
|
||||
|
||||
return site.getDb().getRecord(this.ENTRIES_TABLE, conditions).then(this.parseRecord.bind(this));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the stored add entry data from a certain glossary.
|
||||
*
|
||||
* @param {number} glossaryId Glossary ID.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @param {number} [userId] User the entries belong to. If not defined, current user in site.
|
||||
* @return {Promise<any[]>} Promise resolved with entries.
|
||||
*/
|
||||
getGlossaryNewEntries(glossaryId: number, siteId?: string, userId?: number): Promise<any[]> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const conditions = {
|
||||
glossaryid: glossaryId,
|
||||
userId: userId || site.getUserId(),
|
||||
};
|
||||
|
||||
return site.getDb().getRecords(this.ENTRIES_TABLE, conditions).then((records: any[]) => {
|
||||
return records.map(this.parseRecord.bind(this));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a concept is used offline.
|
||||
*
|
||||
* @param {number} glossaryId Glossary ID.
|
||||
* @param {string} concept Concept to check.
|
||||
* @param {number} [timeCreated] Time of the entry we are editing.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<boolean>} Promise resolved with true if concept is found, false otherwise.
|
||||
*/
|
||||
isConceptUsed(glossaryId: number, concept: string, timeCreated?: number, siteId?: string): Promise<boolean> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const conditions = {
|
||||
glossaryid: glossaryId,
|
||||
concept: concept,
|
||||
};
|
||||
|
||||
return site.getDb().getRecords(this.ENTRIES_TABLE, conditions).then((entries) => {
|
||||
if (!entries.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entries.length > 1 || !timeCreated) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If there's only one entry, check that is not the one we are editing.
|
||||
return this.utils.promiseFails(this.getNewEntry(glossaryId, concept, timeCreated, siteId));
|
||||
});
|
||||
}).catch(() => {
|
||||
// No offline data found, return false.
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a new entry to be sent later.
|
||||
*
|
||||
* @param {number} glossaryId Glossary ID.
|
||||
* @param {string} concept Glossary entry concept.
|
||||
* @param {string} definition Glossary entry concept definition.
|
||||
* @param {number} courseId Course ID of the glossary.
|
||||
* @param {any} [options] Options for the entry.
|
||||
* @param {any} [attachments] Result of CoreFileUploaderProvider#storeFilesToUpload for attachments.
|
||||
* @param {number} [timeCreated] The time the entry was created. If not defined, current time.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @param {number} [userId] User the entry belong to. If not defined, current user in site.
|
||||
* @param {any} [discardEntry] The entry provided will be discarded if found.
|
||||
* @return {Promise<false>} Promise resolved if stored, rejected if failure.
|
||||
*/
|
||||
addNewEntry(glossaryId: number, concept: string, definition: string, courseId: number, options?: any, attachments?: any,
|
||||
timeCreated?: number, siteId?: string, userId?: number, discardEntry?: any): Promise<false> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const entry = {
|
||||
glossaryid: glossaryId,
|
||||
courseid: courseId,
|
||||
concept: concept,
|
||||
definition: definition,
|
||||
definitionformat: 'html',
|
||||
options: JSON.stringify(options),
|
||||
attachments: JSON.stringify(attachments),
|
||||
userid: userId || site.getUserId(),
|
||||
timecreated: timeCreated || new Date().getTime()
|
||||
};
|
||||
|
||||
// If editing an offline entry, delete previous first.
|
||||
let discardPromise;
|
||||
if (discardEntry) {
|
||||
discardPromise = this.deleteNewEntry(glossaryId, discardEntry.concept, discardEntry.timecreated, site.getId());
|
||||
} else {
|
||||
discardPromise = Promise.resolve();
|
||||
}
|
||||
|
||||
return discardPromise.then(() => {
|
||||
return site.getDb().insertRecord(this.ENTRIES_TABLE, entry).then(() => false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the folder where to store files for offline attachments in a glossary.
|
||||
*
|
||||
* @param {number} glossaryId Glossary ID.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<string>} Promise resolved with the path.
|
||||
*/
|
||||
getGlossaryFolder(glossaryId: number, siteId?: string): Promise<string> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const siteFolderPath = this.fileProvider.getSiteFolder(site.getId());
|
||||
const folderPath = 'offlineglossary/' + glossaryId;
|
||||
|
||||
return this.textUtils.concatenatePaths(siteFolderPath, folderPath);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the folder where to store files for a new offline entry.
|
||||
*
|
||||
* @param {number} glossaryId Glossary ID.
|
||||
* @param {string} concept The name of the entry.
|
||||
* @param {number} timeCreated Time to allow duplicated entries.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<string>} Promise resolved with the path.
|
||||
*/
|
||||
getEntryFolder(glossaryId: number, concept: string, timeCreated: number, siteId?: string): Promise<string> {
|
||||
return this.getGlossaryFolder(glossaryId, siteId).then((folderPath) => {
|
||||
return this.textUtils.concatenatePaths(folderPath, 'newentry_' + concept + '_' + timeCreated);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse "options" and "attachments" columns of a fetched record.
|
||||
*
|
||||
* @param {any} records Record object
|
||||
* @return {any} Record object with columns parsed.
|
||||
*/
|
||||
protected parseRecord(record: any): any {
|
||||
record.options = this.textUtils.parseJSON(record.options);
|
||||
record.attachments = this.textUtils.parseJSON(record.attachments);
|
||||
|
||||
return record;
|
||||
}
|
||||
}
|
|
@ -87,6 +87,7 @@ import { AddonModResourceModule } from '@addon/mod/resource/resource.module';
|
|||
import { AddonModFeedbackModule } from '@addon/mod/feedback/feedback.module';
|
||||
import { AddonModFolderModule } from '@addon/mod/folder/folder.module';
|
||||
import { AddonModForumModule } from '@addon/mod/forum/forum.module';
|
||||
import { AddonModGlossaryModule } from '@addon/mod/glossary/glossary.module';
|
||||
import { AddonModPageModule } from '@addon/mod/page/page.module';
|
||||
import { AddonModQuizModule } from '@addon/mod/quiz/quiz.module';
|
||||
import { AddonModScormModule } from '@addon/mod/scorm/scorm.module';
|
||||
|
@ -188,6 +189,7 @@ export const CORE_PROVIDERS: any[] = [
|
|||
AddonModFeedbackModule,
|
||||
AddonModFolderModule,
|
||||
AddonModForumModule,
|
||||
AddonModGlossaryModule,
|
||||
AddonModLtiModule,
|
||||
AddonModPageModule,
|
||||
AddonModQuizModule,
|
||||
|
|
Loading…
Reference in New Issue