forked from CIT/Vmeda.Online
		
	
						commit
						717583dc9b
					
				@ -155,7 +155,7 @@ export class AppComponent implements OnInit, AfterViewInit {
 | 
			
		||||
 | 
			
		||||
                this.lastUrls[url] = Date.now();
 | 
			
		||||
 | 
			
		||||
                CoreEvents.trigger(CoreEvents.APP_LAUNCHED_URL, url);
 | 
			
		||||
                CoreEvents.trigger(CoreEvents.APP_LAUNCHED_URL, { url });
 | 
			
		||||
                CoreCustomURLSchemes.handleCustomURL(url).catch((error) => {
 | 
			
		||||
                    CoreCustomURLSchemes.treatHandleCustomURLError(error);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
@ -137,7 +137,7 @@ export abstract class CorePageItemsListManager<Item> {
 | 
			
		||||
        // If this item is already selected, do nothing.
 | 
			
		||||
        const itemRoute = this.getItemRoute(route);
 | 
			
		||||
        const itemPath = this.getItemPath(item);
 | 
			
		||||
        const selectedItemPath = itemRoute ? this.getSelectedItemPath(itemRoute.snapshot) : null;
 | 
			
		||||
        const selectedItemPath = itemRoute?.snapshot ? this.getSelectedItemPath(itemRoute.snapshot) : null;
 | 
			
		||||
 | 
			
		||||
        if (selectedItemPath === itemPath) {
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
@ -52,6 +52,7 @@ import { CoreAttachmentsComponent } from './attachments/attachments';
 | 
			
		||||
import { CoreFilesComponent } from './files/files';
 | 
			
		||||
import { CoreLocalFileComponent } from './local-file/local-file';
 | 
			
		||||
import { CoreBSTooltipComponent } from './bs-tooltip/bs-tooltip';
 | 
			
		||||
import { CoreSitePickerComponent } from './site-picker/site-picker';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    declarations: [
 | 
			
		||||
@ -86,6 +87,7 @@ import { CoreBSTooltipComponent } from './bs-tooltip/bs-tooltip';
 | 
			
		||||
        CoreFilesComponent,
 | 
			
		||||
        CoreLocalFileComponent,
 | 
			
		||||
        CoreBSTooltipComponent,
 | 
			
		||||
        CoreSitePickerComponent,
 | 
			
		||||
    ],
 | 
			
		||||
    imports: [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
@ -127,6 +129,7 @@ import { CoreBSTooltipComponent } from './bs-tooltip/bs-tooltip';
 | 
			
		||||
        CoreFilesComponent,
 | 
			
		||||
        CoreLocalFileComponent,
 | 
			
		||||
        CoreBSTooltipComponent,
 | 
			
		||||
        CoreSitePickerComponent,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class CoreComponentsModule {}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										6
									
								
								src/core/components/site-picker/core-site-picker.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/core/components/site-picker/core-site-picker.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,6 @@
 | 
			
		||||
<ion-item *ngIf="sites && sites.length">
 | 
			
		||||
    <ion-label>{{ 'core.site' | translate }}</ion-label>
 | 
			
		||||
    <ion-select [(ngModel)]="selectedSite" (ngModelChange)="siteSelected.emit(selectedSite)" interface="action-sheet">
 | 
			
		||||
        <ion-select-option *ngFor="let site of sites" [value]="site.id">{{ site.fullNameAndSiteName }}</ion-select-option>
 | 
			
		||||
    </ion-select>
 | 
			
		||||
</ion-item>
 | 
			
		||||
							
								
								
									
										77
									
								
								src/core/components/site-picker/site-picker.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/core/components/site-picker/site-picker.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,77 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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 { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreFilter } from '@features/filter/services/filter';
 | 
			
		||||
import { CoreSiteBasicInfo, CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { Translate } from '@singletons';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to display a site selector. It will display a select with the list of sites. If the selected site changes,
 | 
			
		||||
 * an output will be emitted with the site ID.
 | 
			
		||||
 *
 | 
			
		||||
 * Example usage:
 | 
			
		||||
 * <core-site-picker (siteSelected)="changeSite($event)"></core-site-picker>
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'core-site-picker',
 | 
			
		||||
    templateUrl: 'core-site-picker.html',
 | 
			
		||||
})
 | 
			
		||||
export class CoreSitePickerComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() initialSite?: string; // Initial site. If not provided, current site.
 | 
			
		||||
    @Output() siteSelected = new EventEmitter<string>(); // Emit an event when a site is selected. Sends the siteId as parameter.
 | 
			
		||||
 | 
			
		||||
    selectedSite?: string;
 | 
			
		||||
    sites?: SiteInfo[];
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async ngOnInit(): Promise<void> {
 | 
			
		||||
        this.selectedSite = this.initialSite || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        // Load the sites.
 | 
			
		||||
        const sites = await CoreSites.getSites();
 | 
			
		||||
 | 
			
		||||
        if (!this.selectedSite && sites.length) {
 | 
			
		||||
            // There is no current site, select the first one.
 | 
			
		||||
            this.selectedSite = sites[0].id;
 | 
			
		||||
            this.siteSelected.emit(this.selectedSite);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await Promise.all(sites.map(async (site: SiteInfo) => {
 | 
			
		||||
            // Format the site name.
 | 
			
		||||
            const options = { clean: true, singleLine: true, filter: false };
 | 
			
		||||
            const siteName = await CoreUtils.ignoreErrors(
 | 
			
		||||
                CoreFilter.formatText(site.siteName || '', options, [], site.id),
 | 
			
		||||
                site.siteName || '',
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            site.fullNameAndSiteName = Translate.instant(
 | 
			
		||||
                'core.fullnameandsitename',
 | 
			
		||||
                { fullname: site.fullName, sitename: siteName },
 | 
			
		||||
            );
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this.sites = sites;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SiteInfo = CoreSiteBasicInfo & {
 | 
			
		||||
    fullNameAndSiteName?: string;
 | 
			
		||||
};
 | 
			
		||||
@ -57,7 +57,7 @@ import { CORE_LOGIN_SERVICES } from '@features/login/login.module';
 | 
			
		||||
import { CORE_MAINMENU_SERVICES } from '@features/mainmenu/mainmenu.module';
 | 
			
		||||
import { CORE_PUSHNOTIFICATIONS_SERVICES } from '@features/pushnotifications/pushnotifications.module';
 | 
			
		||||
import { CORE_QUESTION_SERVICES } from '@features/question/question.module';
 | 
			
		||||
// @todo import { CORE_SHAREDFILES_SERVICES } from '@features/sharedfiles/sharedfiles.module';
 | 
			
		||||
import { CORE_SHAREDFILES_SERVICES } from '@features/sharedfiles/sharedfiles.module';
 | 
			
		||||
import { CORE_RATING_SERVICES } from '@features/rating/rating.module';
 | 
			
		||||
import { CORE_SEARCH_SERVICES } from '@features/search/search.module';
 | 
			
		||||
import { CORE_SETTINGS_SERVICES } from '@features/settings/settings.module';
 | 
			
		||||
@ -271,7 +271,7 @@ export class CoreCompileProvider {
 | 
			
		||||
            ...CORE_RATING_SERVICES,
 | 
			
		||||
            ...CORE_SEARCH_SERVICES,
 | 
			
		||||
            ...CORE_SETTINGS_SERVICES,
 | 
			
		||||
            // @todo ...CORE_SHAREDFILES_SERVICES,
 | 
			
		||||
            ...CORE_SHAREDFILES_SERVICES,
 | 
			
		||||
            ...CORE_SITEHOME_SERVICES,
 | 
			
		||||
            CoreSitePluginsProvider,
 | 
			
		||||
            ...CORE_TAG_SERVICES,
 | 
			
		||||
 | 
			
		||||
@ -33,6 +33,7 @@ import { CoreSearchModule } from './search/search.module';
 | 
			
		||||
import { CoreCommentsModule } from './comments/comments.module';
 | 
			
		||||
import { CoreSitePluginsModule } from './siteplugins/siteplugins.module';
 | 
			
		||||
import { CoreRatingModule } from './rating/rating.module';
 | 
			
		||||
import { CoreSharedFilesModule } from './sharedfiles/sharedfiles.module';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
@ -55,6 +56,7 @@ import { CoreRatingModule } from './rating/rating.module';
 | 
			
		||||
        CoreCommentsModule,
 | 
			
		||||
        CoreSitePluginsModule,
 | 
			
		||||
        CoreRatingModule,
 | 
			
		||||
        CoreSharedFilesModule,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class CoreFeaturesModule {}
 | 
			
		||||
 | 
			
		||||
@ -376,16 +376,14 @@ export class CoreFileUploaderHelperProvider {
 | 
			
		||||
                    try {
 | 
			
		||||
                        const data = await handler.action(maxSize, upload, allowOffline, handler.mimetypes);
 | 
			
		||||
 | 
			
		||||
                        let result: CoreWSUploadFileResult | FileEntry | undefined;
 | 
			
		||||
 | 
			
		||||
                        if (data.treated) {
 | 
			
		||||
                            // The handler already treated the file. Return the result.
 | 
			
		||||
                            this.fileUploaded(data.result!);
 | 
			
		||||
 | 
			
		||||
                            return true;
 | 
			
		||||
                            result = data.result;
 | 
			
		||||
                        } else if (data.fileEntry) {
 | 
			
		||||
                            // The handler provided us a fileEntry, use it.
 | 
			
		||||
                            await this.uploadFileEntry(data.fileEntry, !!data.delete, maxSize, upload, allowOffline);
 | 
			
		||||
 | 
			
		||||
                            return true;
 | 
			
		||||
                            result = await this.uploadFileEntry(data.fileEntry, !!data.delete, maxSize, upload, allowOffline);
 | 
			
		||||
                        } else if (data.path) {
 | 
			
		||||
                            let fileEntry: FileEntry;
 | 
			
		||||
 | 
			
		||||
@ -398,13 +396,17 @@ export class CoreFileUploaderHelperProvider {
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            // File found, treat it.
 | 
			
		||||
                            await this.uploadFileEntry(fileEntry, !!data.delete, maxSize, upload, allowOffline);
 | 
			
		||||
 | 
			
		||||
                            return true;
 | 
			
		||||
                            result = await this.uploadFileEntry(fileEntry, !!data.delete, maxSize, upload, allowOffline);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        // Nothing received, fail.
 | 
			
		||||
                        throw new CoreError('No file received');
 | 
			
		||||
                        if (!result) {
 | 
			
		||||
                            // Nothing received, fail.
 | 
			
		||||
                            throw new CoreError('No file received');
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        this.fileUploaded(result);
 | 
			
		||||
 | 
			
		||||
                        return true;
 | 
			
		||||
                    } catch (error) {
 | 
			
		||||
                        CoreDomUtils.showErrorModalDefault(
 | 
			
		||||
                            error,
 | 
			
		||||
 | 
			
		||||
@ -1,55 +0,0 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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 { CoreConstants } from '@/core/constants';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Settings section.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreSettingsSection = {
 | 
			
		||||
    name: string;
 | 
			
		||||
    path: string;
 | 
			
		||||
    icon: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Settings constants.
 | 
			
		||||
 */
 | 
			
		||||
export class CoreSettingsConstants {
 | 
			
		||||
 | 
			
		||||
    static readonly SECTIONS: CoreSettingsSection[] = [
 | 
			
		||||
        {
 | 
			
		||||
            name: 'general',
 | 
			
		||||
            path: 'general',
 | 
			
		||||
            icon: 'fas-wrench',
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            name: 'spaceusage',
 | 
			
		||||
            path: 'spaceusage',
 | 
			
		||||
            icon: 'fas-tasks',
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            name: 'synchronization',
 | 
			
		||||
            path: 'sync',
 | 
			
		||||
            icon: CoreConstants.ICON_SYNC,
 | 
			
		||||
        },
 | 
			
		||||
        // @TODO sharedfiles
 | 
			
		||||
        {
 | 
			
		||||
            name: 'about',
 | 
			
		||||
            path: 'about',
 | 
			
		||||
            icon: 'fas-id-card',
 | 
			
		||||
        },
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -10,15 +10,10 @@
 | 
			
		||||
<ion-content>
 | 
			
		||||
    <core-split-view>
 | 
			
		||||
        <ion-list>
 | 
			
		||||
            <ion-item
 | 
			
		||||
                *ngFor="let section of sections.items"
 | 
			
		||||
                [class.core-selected-item]="sections.isSelected(section)"
 | 
			
		||||
                button
 | 
			
		||||
                detail
 | 
			
		||||
                (click)="sections.select(section)"
 | 
			
		||||
            >
 | 
			
		||||
            <ion-item *ngFor="let section of sections.items" [class.core-selected-item]="sections.isSelected(section)" button
 | 
			
		||||
                detail="true" (click)="sections.select(section)">
 | 
			
		||||
                <ion-icon [name]="section.icon" slot="start"></ion-icon>
 | 
			
		||||
                <ion-label>{{ 'core.settings.' + section.name | translate }}</ion-label>
 | 
			
		||||
                <ion-label>{{ section.name | translate }}</ion-label>
 | 
			
		||||
            </ion-item>
 | 
			
		||||
        </ion-list>
 | 
			
		||||
    </core-split-view>
 | 
			
		||||
 | 
			
		||||
@ -13,10 +13,14 @@
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { AfterViewInit, Component, OnDestroy, ViewChild } from '@angular/core';
 | 
			
		||||
import { CoreSettingsConstants, CoreSettingsSection } from '@features/settings/constants';
 | 
			
		||||
import { ActivatedRouteSnapshot, Params } from '@angular/router';
 | 
			
		||||
 | 
			
		||||
import { CorePageItemsListManager } from '@classes/page-items-list-manager';
 | 
			
		||||
import { ActivatedRouteSnapshot } from '@angular/router';
 | 
			
		||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
 | 
			
		||||
import { CoreSettingsHelper } from '@features/settings/services/settings-helper';
 | 
			
		||||
import { CoreConstants } from '@/core/constants';
 | 
			
		||||
import { SHAREDFILES_PAGE_NAME } from '@features/sharedfiles/sharedfiles.module';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'page-core-settings-index',
 | 
			
		||||
@ -32,7 +36,7 @@ export class CoreSettingsIndexPage implements AfterViewInit, OnDestroy {
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    ngAfterViewInit(): void {
 | 
			
		||||
        this.sections.setItems(CoreSettingsConstants.SECTIONS);
 | 
			
		||||
        this.sections.setItems(this.getSections());
 | 
			
		||||
        this.sections.start(this.splitView);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -43,6 +47,48 @@ export class CoreSettingsIndexPage implements AfterViewInit, OnDestroy {
 | 
			
		||||
        this.sections.destroy();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the sections.
 | 
			
		||||
     *
 | 
			
		||||
     * @returns Sections.
 | 
			
		||||
     */
 | 
			
		||||
    protected getSections(): CoreSettingsSection[] {
 | 
			
		||||
        const sections: CoreSettingsSection[] = [
 | 
			
		||||
            {
 | 
			
		||||
                name: 'core.settings.general',
 | 
			
		||||
                path: 'general',
 | 
			
		||||
                icon: 'fas-wrench',
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                name: 'core.settings.spaceusage',
 | 
			
		||||
                path: 'spaceusage',
 | 
			
		||||
                icon: 'fas-tasks',
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                name: 'core.settings.synchronization',
 | 
			
		||||
                path: 'sync',
 | 
			
		||||
                icon: CoreConstants.ICON_SYNC,
 | 
			
		||||
            },
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        if (CoreApp.isIOS()) {
 | 
			
		||||
            sections.push({
 | 
			
		||||
                name: 'core.sharedfiles.sharedfiles',
 | 
			
		||||
                path: SHAREDFILES_PAGE_NAME + '/list/root',
 | 
			
		||||
                icon: 'fas-folder',
 | 
			
		||||
                params: { manage: true },
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        sections.push({
 | 
			
		||||
            name: 'core.settings.about',
 | 
			
		||||
            path: 'about',
 | 
			
		||||
            icon: 'fas-id-card',
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return sections;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -57,11 +103,28 @@ class CoreSettingsSectionsManager extends CorePageItemsListManager<CoreSettingsS
 | 
			
		||||
        return section.path;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    protected getItemQueryParams(section: CoreSettingsSection): Params {
 | 
			
		||||
        return section.params || {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    protected getSelectedItemPath(route: ActivatedRouteSnapshot): string | null {
 | 
			
		||||
        return route.parent?.routeConfig?.path ?? null;
 | 
			
		||||
        return CoreSettingsHelper.getSelectedItemPath(route);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Settings section.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreSettingsSection = {
 | 
			
		||||
    name: string;
 | 
			
		||||
    path: string;
 | 
			
		||||
    icon: string;
 | 
			
		||||
    params?: Params;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -26,19 +26,9 @@
 | 
			
		||||
                    </ion-label>
 | 
			
		||||
                </ion-item>
 | 
			
		||||
                <ion-item-divider><ion-label></ion-label></ion-item-divider>
 | 
			
		||||
                <!-- <ion-item *ngIf="isIOS"
 | 
			
		||||
                    (click)="openHandler('CoreSharedFilesListPage', {manage: true, siteId: siteId, hideSitePicker: true})"
 | 
			
		||||
                    [title]="'core.sharedfiles.sharedfiles' | translate"
 | 
			
		||||
                    [class.core-selected-item]="'CoreSharedFilesListPage' == selectedPage" detail>
 | 
			
		||||
                    <ion-icon name="fas-folder" slot="start"></ion-icon>
 | 
			
		||||
                    <ion-label>
 | 
			
		||||
                        <h2>{{ 'core.sharedfiles.sharedfiles' | translate }}</h2>
 | 
			
		||||
                    </ion-label>
 | 
			
		||||
                    <ion-badge slot="end">{{ iosSharedFiles }}</ion-badge>
 | 
			
		||||
                </ion-item> -->
 | 
			
		||||
 | 
			
		||||
                <ion-item *ngFor="let handler of handlers.items" [ngClass]="['core-settings-handler', handler.class]"
 | 
			
		||||
                    [title]="handler.title | translate" detail="true" (click)="handlers.select(handler)"
 | 
			
		||||
                    [title]="handler.title | translate" detail="true" (click)="handlers.select(handler)" button
 | 
			
		||||
                    [class.core-selected-item]="handlers.isSelected(handler)">
 | 
			
		||||
                    <ion-icon [name]="handler.icon" slot="start" *ngIf="handler.icon">
 | 
			
		||||
                    </ion-icon>
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,6 @@ import { CoreSettingsDelegate, CoreSettingsHandlerToDisplay } from '../../servic
 | 
			
		||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
// import { CoreSharedFiles } from '@features/sharedfiles/services/sharedfiles';
 | 
			
		||||
import { CoreSettingsHelper, CoreSiteSpaceUsage } from '../../services/settings-helper';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreSiteInfo } from '@classes/site';
 | 
			
		||||
@ -52,7 +51,6 @@ export class CoreSitePreferencesPage implements AfterViewInit, OnDestroy {
 | 
			
		||||
        spaceUsage: 0,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    iosSharedFiles = 0;
 | 
			
		||||
    protected sitesObserver: CoreEventObserver;
 | 
			
		||||
    protected isDestroyed = false;
 | 
			
		||||
 | 
			
		||||
@ -101,25 +99,7 @@ export class CoreSitePreferencesPage implements AfterViewInit, OnDestroy {
 | 
			
		||||
        this.siteName = currentSite!.getSiteName();
 | 
			
		||||
        this.siteUrl = currentSite!.getURL();
 | 
			
		||||
 | 
			
		||||
        const promises: Promise<void>[] = [];
 | 
			
		||||
 | 
			
		||||
        promises.push(CoreSettingsHelper.getSiteSpaceUsage(this.siteId)
 | 
			
		||||
            .then((spaceUsage) => {
 | 
			
		||||
                this.spaceUsage = spaceUsage;
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        /* if (this.isIOS) {
 | 
			
		||||
            promises.push(CoreSharedFiles.getSiteSharedFiles(this.siteId)
 | 
			
		||||
                .then((files) => {
 | 
			
		||||
                this.iosSharedFiles = files.length;
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }));
 | 
			
		||||
        }*/
 | 
			
		||||
 | 
			
		||||
        await Promise.all(promises);
 | 
			
		||||
        this.spaceUsage = await CoreSettingsHelper.getSiteSpaceUsage(this.siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -224,9 +204,7 @@ class CoreSettingsSitePreferencesManager extends CorePageItemsListManager<CoreSe
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    protected getSelectedItemPath(route: ActivatedRouteSnapshot): string | null {
 | 
			
		||||
        // @todo: routeConfig doesn't have a path after refreshing the app.
 | 
			
		||||
        // route.component is null too, and route.parent.url is empty.
 | 
			
		||||
        return route.parent?.routeConfig?.path ?? null;
 | 
			
		||||
        return CoreSettingsHelper.getSelectedItemPath(route);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -27,6 +27,9 @@ import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CoreCourse } from '@features/course/services/course';
 | 
			
		||||
import { makeSingleton, Translate } from '@singletons';
 | 
			
		||||
import { CoreError } from '@classes/errors/error';
 | 
			
		||||
import { ActivatedRouteSnapshot } from '@angular/router';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Object with space usage and cache entries that can be erased.
 | 
			
		||||
@ -438,6 +441,29 @@ export class CoreSettingsHelperProvider {
 | 
			
		||||
        document.body.classList.toggle('dark', enable);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Implementation of getSelectedItemPath for settings items managers.
 | 
			
		||||
     *
 | 
			
		||||
     * @param route Current route.
 | 
			
		||||
     * @return Path of the selected item in the given route.
 | 
			
		||||
     */
 | 
			
		||||
    getSelectedItemPath(route: ActivatedRouteSnapshot): string | null {
 | 
			
		||||
        // @todo: routeConfig doesn't have a path after refreshing the app.
 | 
			
		||||
        // route.component is null too, and route.parent.url is empty.
 | 
			
		||||
        let routePath = route.routeConfig?.path;
 | 
			
		||||
        const parentPath = route.parent?.routeConfig?.path;
 | 
			
		||||
 | 
			
		||||
        if (!routePath && !parentPath) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (routePath) {
 | 
			
		||||
            routePath = CoreNavigator.replaceRoutePathParams(routePath, route.params);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return CoreTextUtils.concatenatePaths(parentPath || '', routePath || '');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const CoreSettingsHelper = makeSingleton(CoreSettingsHelperProvider);
 | 
			
		||||
 | 
			
		||||
@ -20,6 +20,7 @@ import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreScreen } from '@services/screen';
 | 
			
		||||
 | 
			
		||||
import { CoreSettingsIndexPage } from './pages/index';
 | 
			
		||||
import { SHAREDFILES_PAGE_NAME } from '@features/sharedfiles/sharedfiles.module';
 | 
			
		||||
 | 
			
		||||
const sectionRoutes: Routes = [
 | 
			
		||||
    {
 | 
			
		||||
@ -36,7 +37,10 @@ const sectionRoutes: Routes = [
 | 
			
		||||
            import('./pages/synchronization/synchronization.module')
 | 
			
		||||
                .then(m => m.CoreSettingsSynchronizationPageModule),
 | 
			
		||||
    },
 | 
			
		||||
    // @todo sharedfiles
 | 
			
		||||
    {
 | 
			
		||||
        path: SHAREDFILES_PAGE_NAME,
 | 
			
		||||
        loadChildren: () => import('@features/sharedfiles/sharedfiles-lazy.module').then(m => m.CoreSharedFilesLazyModule),
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        path: 'about',
 | 
			
		||||
        loadChildren: () => import('./pages/about/about.module').then(m => m.CoreSettingsAboutPageModule),
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,34 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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 { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreSharedFilesListComponent } from './list/list';
 | 
			
		||||
import { CoreSharedFilesListModalComponent } from './list-modal/list-modal';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    declarations: [
 | 
			
		||||
        CoreSharedFilesListComponent,
 | 
			
		||||
        CoreSharedFilesListModalComponent,
 | 
			
		||||
    ],
 | 
			
		||||
    imports: [
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        CoreSharedFilesListComponent,
 | 
			
		||||
        CoreSharedFilesListModalComponent,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class CoreSharedFilesComponentsModule {}
 | 
			
		||||
@ -0,0 +1,19 @@
 | 
			
		||||
<ion-header>
 | 
			
		||||
    <ion-toolbar>
 | 
			
		||||
        <ion-buttons slot="start">
 | 
			
		||||
            <ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
 | 
			
		||||
        </ion-buttons>
 | 
			
		||||
        <ion-title>{{ title }}</ion-title>
 | 
			
		||||
 | 
			
		||||
        <ion-buttons slot="end">
 | 
			
		||||
            <ion-button (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
 | 
			
		||||
                <ion-icon name="fas-times" slot="icon-only"></ion-icon>
 | 
			
		||||
            </ion-button>
 | 
			
		||||
        </ion-buttons>
 | 
			
		||||
    </ion-toolbar>
 | 
			
		||||
</ion-header>
 | 
			
		||||
<ion-content>
 | 
			
		||||
    <core-shared-files-list [siteId]="siteId" [mimetypes]="mimetypes" [isModal]="true" [manage]="manage" [pick]="pick"
 | 
			
		||||
        [path]="path" [showSitePicker]="showSitePicker" (onPathChanged)="calculateTitle($event)" (onFilePicked)="filePicked($event)">
 | 
			
		||||
    </core-shared-files-list>
 | 
			
		||||
</ion-content>
 | 
			
		||||
@ -0,0 +1,75 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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 { Component, OnInit, Input } from '@angular/core';
 | 
			
		||||
import { FileEntry } from '@ionic-native/file';
 | 
			
		||||
 | 
			
		||||
import { CoreFile } from '@services/file';
 | 
			
		||||
import { ModalController, Translate } from '@singletons';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Modal to display the list of shared files.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'core-shared-files-list-modal',
 | 
			
		||||
    templateUrl: 'list-modal.html',
 | 
			
		||||
})
 | 
			
		||||
export class CoreSharedFilesListModalComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    @Input() siteId?: string;
 | 
			
		||||
    @Input() mimetypes?: string[];
 | 
			
		||||
    @Input() manage?: boolean;
 | 
			
		||||
    @Input() pick?: boolean; // To pick a file you MUST use a modal.
 | 
			
		||||
    @Input() path?: string;
 | 
			
		||||
    @Input() showSitePicker?: boolean;
 | 
			
		||||
 | 
			
		||||
    title?: string;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Component being initialized.
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        this.calculateTitle(this.path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Calculate the title.
 | 
			
		||||
     *
 | 
			
		||||
     * @param path Path to use.
 | 
			
		||||
     */
 | 
			
		||||
    calculateTitle(path?: string): void {
 | 
			
		||||
        if (path) {
 | 
			
		||||
            this.title = CoreFile.getFileAndDirectoryFromPath(path).name;
 | 
			
		||||
        } else {
 | 
			
		||||
            this.title = Translate.instant('core.sharedfiles.sharedfiles');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Close modal.
 | 
			
		||||
     */
 | 
			
		||||
    closeModal(): void {
 | 
			
		||||
        ModalController.dismiss();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A file was picked.
 | 
			
		||||
     *
 | 
			
		||||
     * @param file Picked file.
 | 
			
		||||
     */
 | 
			
		||||
    filePicked(file: FileEntry): void {
 | 
			
		||||
        ModalController.dismiss(file);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								src/core/features/sharedfiles/components/list/list.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/core/features/sharedfiles/components/list/list.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
			
		||||
<ion-refresher slot="fixed" [disabled]="!filesLoaded" (ionRefresh)="refreshFiles($event.target)">
 | 
			
		||||
    <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
 | 
			
		||||
</ion-refresher>
 | 
			
		||||
 | 
			
		||||
<!-- Allow selecting the site to view. -->
 | 
			
		||||
<core-site-picker *ngIf="showSitePicker" [hidden]="!filesLoaded" [initialSite]="siteId" (siteSelected)="changeSite($event)">
 | 
			
		||||
</core-site-picker>
 | 
			
		||||
 | 
			
		||||
<core-loading [hideUntil]="filesLoaded" class="core-loading-center">
 | 
			
		||||
    <ion-list *ngIf="files && files.length > 0">
 | 
			
		||||
        <ng-container *ngFor="let file of files; let idx = index">
 | 
			
		||||
            <core-local-file *ngIf="file.isFile" [file]="file" [manage]="manage" [overrideClick]="pick"
 | 
			
		||||
                (onClick)="filePicked(file)" (onDelete)="fileDeleted(idx)" (onRename)="fileRenamed(idx, $event)">
 | 
			
		||||
            </core-local-file>
 | 
			
		||||
 | 
			
		||||
            <ion-item button *ngIf="!file.isFile" class="ion-text-wrap item-file" (click)="openFolder(file)">
 | 
			
		||||
                <ion-thumbnail slot="start" aria-hidden="true">
 | 
			
		||||
                    <img src="assets/img/files/folder-64.png" alt="">
 | 
			
		||||
                </ion-thumbnail>
 | 
			
		||||
                <ion-label>{{ file.name }}</ion-label>
 | 
			
		||||
            </ion-item>
 | 
			
		||||
        </ng-container>
 | 
			
		||||
    </ion-list>
 | 
			
		||||
 | 
			
		||||
    <core-empty-box *ngIf="files && !files.length && manage" icon="fas-folder"
 | 
			
		||||
        [message]="'core.sharedfiles.nosharedfiles' | translate">
 | 
			
		||||
    </core-empty-box>
 | 
			
		||||
 | 
			
		||||
    <core-empty-box *ngIf="files && !files.length && !manage" icon="fas-folder"
 | 
			
		||||
        [message]="'core.sharedfiles.nosharedfilestoupload' | translate">
 | 
			
		||||
    </core-empty-box>
 | 
			
		||||
</core-loading>
 | 
			
		||||
							
								
								
									
										170
									
								
								src/core/features/sharedfiles/components/list/list.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								src/core/features/sharedfiles/components/list/list.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,170 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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 { Component, OnInit, OnDestroy, Input, Output, EventEmitter } from '@angular/core';
 | 
			
		||||
import { FileEntry, DirectoryEntry } from '@ionic-native/file';
 | 
			
		||||
import { IonRefresher } from '@ionic/angular';
 | 
			
		||||
import { Md5 } from 'ts-md5';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedFiles } from '@features/sharedfiles/services/sharedfiles';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component to display the list of shared files, either as a modal or inside a page.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'core-shared-files-list',
 | 
			
		||||
    templateUrl: 'list.html',
 | 
			
		||||
})
 | 
			
		||||
export class CoreSharedFilesListComponent implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
    @Input() siteId?: string;
 | 
			
		||||
    @Input() mimetypes?: string[];
 | 
			
		||||
    @Input() isModal?: boolean; // Whether the component is loaded in a modal.
 | 
			
		||||
    @Input() manage?: boolean;
 | 
			
		||||
    @Input() pick?: boolean; // To pick a file you MUST use a modal.
 | 
			
		||||
    @Input() path?: string;
 | 
			
		||||
    @Input() showSitePicker?: boolean;
 | 
			
		||||
    @Output() onPathChanged = new EventEmitter<string>();
 | 
			
		||||
    @Output() onFilePicked = new EventEmitter<FileEntry>();
 | 
			
		||||
 | 
			
		||||
    filesLoaded = false;
 | 
			
		||||
    files?: (FileEntry | DirectoryEntry)[];
 | 
			
		||||
 | 
			
		||||
    protected shareObserver?: CoreEventObserver;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Component being initialized.
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        this.siteId = this.siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        this.loadFiles();
 | 
			
		||||
 | 
			
		||||
        // Listen for new files shared with the app.
 | 
			
		||||
        this.shareObserver = CoreEvents.on(CoreEvents.FILE_SHARED, (data) => {
 | 
			
		||||
            if (data.siteId == this.siteId) {
 | 
			
		||||
                // File was stored in current site, refresh the list.
 | 
			
		||||
                this.filesLoaded = false;
 | 
			
		||||
                this.loadFiles().finally(() => {
 | 
			
		||||
                    this.filesLoaded = true;
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Load the files.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async loadFiles(): Promise<void> {
 | 
			
		||||
        this.files = await CoreSharedFiles.getSiteSharedFiles(this.siteId, this.path, this.mimetypes);
 | 
			
		||||
        this.filesLoaded = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Refresh the list of files.
 | 
			
		||||
     *
 | 
			
		||||
     * @param refresher Refresher.
 | 
			
		||||
     */
 | 
			
		||||
    refreshFiles(refresher: IonRefresher): void {
 | 
			
		||||
        this.loadFiles().finally(() => {
 | 
			
		||||
            refresher.complete();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when a file is deleted. Remove the file from the list.
 | 
			
		||||
     *
 | 
			
		||||
     * @param index Position of the file.
 | 
			
		||||
     */
 | 
			
		||||
    fileDeleted(index: number): void {
 | 
			
		||||
        this.files!.splice(index, 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when a file is renamed. Update the list.
 | 
			
		||||
     *
 | 
			
		||||
     * @param index Position of the file.
 | 
			
		||||
     * @param data Data containing the new FileEntry.
 | 
			
		||||
     */
 | 
			
		||||
    fileRenamed(index: number, data: { file: FileEntry }): void {
 | 
			
		||||
        this.files![index] = data.file;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Open a subfolder.
 | 
			
		||||
     *
 | 
			
		||||
     * @param folder The folder to open.
 | 
			
		||||
     */
 | 
			
		||||
    openFolder(folder: DirectoryEntry): void {
 | 
			
		||||
        const path = CoreTextUtils.concatenatePaths(this.path || '', folder.name);
 | 
			
		||||
 | 
			
		||||
        if (this.isModal) {
 | 
			
		||||
            this.path = path;
 | 
			
		||||
            this.filesLoaded = false;
 | 
			
		||||
            this.loadFiles();
 | 
			
		||||
            this.onPathChanged.emit(path);
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const hash = <string> Md5.hashAsciiStr(path);
 | 
			
		||||
 | 
			
		||||
        CoreNavigator.navigate(`../${hash}`, {
 | 
			
		||||
            params: {
 | 
			
		||||
                path,
 | 
			
		||||
                manage: this.manage,
 | 
			
		||||
                pick: this.pick,
 | 
			
		||||
                siteId: this.siteId,
 | 
			
		||||
                mimetypes: this.mimetypes,
 | 
			
		||||
                isModal: false,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Change site loaded.
 | 
			
		||||
     *
 | 
			
		||||
     * @param id Site to load.
 | 
			
		||||
     */
 | 
			
		||||
    changeSite(id: string): void {
 | 
			
		||||
        this.siteId = id;
 | 
			
		||||
        this.path = '';
 | 
			
		||||
        this.filesLoaded = false;
 | 
			
		||||
        this.loadFiles();
 | 
			
		||||
        this.onPathChanged.emit('');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A file was picked.
 | 
			
		||||
     *
 | 
			
		||||
     * @param file Picked file.
 | 
			
		||||
     */
 | 
			
		||||
    filePicked(file: FileEntry): void {
 | 
			
		||||
        this.onFilePicked.emit(file);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Component destroyed.
 | 
			
		||||
     */
 | 
			
		||||
    ngOnDestroy(): void {
 | 
			
		||||
        this.shareObserver?.off();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								src/core/features/sharedfiles/lang.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/core/features/sharedfiles/lang.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
{
 | 
			
		||||
    "chooseaccountstorefile": "Choose an account to store the file in.",
 | 
			
		||||
    "chooseactionrepeatedfile": "A file with this name already exists. Do you want to replace the existing file or rename it to \"{{$a}}\"?",
 | 
			
		||||
    "errorreceivefilenosites": "There are no sites stored. Please add a site before sharing a file with the app.",
 | 
			
		||||
    "nosharedfiles": "There are no shared files stored on this site.",
 | 
			
		||||
    "nosharedfilestoupload": "You have no files to upload here. If you want to upload a file from another app, locate the file and click the 'Open in' button.",
 | 
			
		||||
    "rename": "Rename",
 | 
			
		||||
    "replace": "Replace",
 | 
			
		||||
    "sharedfiles": "Shared files",
 | 
			
		||||
    "successstorefile": "File successfully stored. Select the file to upload to your private files or use in an activity."
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,32 @@
 | 
			
		||||
<ion-header>
 | 
			
		||||
    <ion-toolbar>
 | 
			
		||||
        <ion-buttons slot="start">
 | 
			
		||||
            <ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
 | 
			
		||||
        </ion-buttons>
 | 
			
		||||
        <ion-title>{{ 'core.sharedfiles.sharedfiles' | translate }}</ion-title>
 | 
			
		||||
    </ion-toolbar>
 | 
			
		||||
</ion-header>
 | 
			
		||||
<ion-content>
 | 
			
		||||
    <core-loading [hideUntil]="loaded">
 | 
			
		||||
        <ion-list>
 | 
			
		||||
            <ion-item class="ion-text-wrap">
 | 
			
		||||
                <ion-label>
 | 
			
		||||
                    <h2>{{ 'core.sharedfiles.chooseaccountstorefile' | translate }}</h2>
 | 
			
		||||
                    <p>{{fileName}}</p>
 | 
			
		||||
                </ion-label>
 | 
			
		||||
            </ion-item>
 | 
			
		||||
            <ion-item *ngFor="let site of sites" (click)="storeInSite(site.id)" detail="false">
 | 
			
		||||
                <ion-avatar slot="start" aria-hidden="true">
 | 
			
		||||
                    <img [src]="site.avatar" core-external-content [siteId]="site.id"
 | 
			
		||||
                        alt="{{ 'core.pictureof' | translate:{$a: site.fullname} }}" role="presentation"
 | 
			
		||||
                        onError="this.src='assets/img/user-avatar.png'">
 | 
			
		||||
                </ion-avatar>
 | 
			
		||||
                <ion-label>
 | 
			
		||||
                    <h2>{{site.fullName}}</h2>
 | 
			
		||||
                    <p><core-format-text clean="true" [text]="site.siteName" [siteId]="site.id"></core-format-text></p>
 | 
			
		||||
                    <p>{{site.siteUrl}}</p>
 | 
			
		||||
                </ion-label>
 | 
			
		||||
            </ion-item>
 | 
			
		||||
        </ion-list>
 | 
			
		||||
    </core-loading>
 | 
			
		||||
</ion-content>
 | 
			
		||||
							
								
								
									
										108
									
								
								src/core/features/sharedfiles/pages/choose-site/choose-site.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								src/core/features/sharedfiles/pages/choose-site/choose-site.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,108 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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 { Component, OnInit } from '@angular/core';
 | 
			
		||||
import { CoreSharedFilesHelper } from '@features/sharedfiles/services/sharedfiles-helper';
 | 
			
		||||
import { FileEntry } from '@ionic-native/file';
 | 
			
		||||
import { CoreFile } from '@services/file';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreSiteBasicInfo, CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Page to display the list of sites to choose one to store a shared file.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'page-core-shared-files-choose-site',
 | 
			
		||||
    templateUrl: 'choose-site.html',
 | 
			
		||||
})
 | 
			
		||||
export class CoreSharedFilesChooseSitePage implements OnInit {
 | 
			
		||||
 | 
			
		||||
    fileName?: string;
 | 
			
		||||
    sites?: CoreSiteBasicInfo[];
 | 
			
		||||
    loaded = false;
 | 
			
		||||
 | 
			
		||||
    protected filePath?: string;
 | 
			
		||||
    protected fileEntry?: FileEntry;
 | 
			
		||||
    protected isInbox = false; // Whether the file is in the Inbox folder.
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async ngOnInit(): Promise<void> {
 | 
			
		||||
        this.filePath = CoreNavigator.getRouteParam('filePath');
 | 
			
		||||
        this.isInbox = !!CoreNavigator.getRouteBooleanParam('isInbox');
 | 
			
		||||
 | 
			
		||||
        if (!this.filePath) {
 | 
			
		||||
            CoreDomUtils.showErrorModal('Error reading file.');
 | 
			
		||||
            await CoreUtils.nextTick();
 | 
			
		||||
            CoreNavigator.back();
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const fileAndDir = CoreFile.getFileAndDirectoryFromPath(this.filePath);
 | 
			
		||||
        this.fileName = fileAndDir.name;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await Promise.all([
 | 
			
		||||
                this.loadFile(),
 | 
			
		||||
                this.loadSites(),
 | 
			
		||||
            ]);
 | 
			
		||||
        } catch {
 | 
			
		||||
            CoreDomUtils.showErrorModal('Error reading file.');
 | 
			
		||||
            CoreNavigator.back();
 | 
			
		||||
        } finally {
 | 
			
		||||
            this.loaded = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Load the file data.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async loadFile(): Promise<void> {
 | 
			
		||||
        this.fileEntry = await CoreFile.getExternalFile(this.filePath!);
 | 
			
		||||
        this.fileName = this.fileEntry.name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Load sites.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async loadSites(): Promise<void> {
 | 
			
		||||
        this.sites = await CoreSites.getSites();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Store the file in a certain site.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId Site ID.
 | 
			
		||||
     */
 | 
			
		||||
    async storeInSite(siteId: string): Promise<void> {
 | 
			
		||||
        this.loaded = false;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await CoreSharedFilesHelper.storeSharedFileInSite(this.fileEntry!, siteId, this.isInbox);
 | 
			
		||||
 | 
			
		||||
            CoreNavigator.back();
 | 
			
		||||
        } finally {
 | 
			
		||||
            this.loaded = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								src/core/features/sharedfiles/pages/list/list.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/core/features/sharedfiles/pages/list/list.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
			
		||||
<ion-header>
 | 
			
		||||
    <ion-toolbar>
 | 
			
		||||
        <ion-buttons slot="start">
 | 
			
		||||
            <ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
 | 
			
		||||
        </ion-buttons>
 | 
			
		||||
        <ion-title>{{ title }}</ion-title>
 | 
			
		||||
    </ion-toolbar>
 | 
			
		||||
</ion-header>
 | 
			
		||||
<ion-content>
 | 
			
		||||
    <core-shared-files-list [siteId]="siteId" [mimetypes]="mimetypes" [isModal]="false" [manage]="manage" [pick]="false"
 | 
			
		||||
        [path]="path" [showSitePicker]="showSitePicker" (onPathChanged)="calculateTitle($event)">
 | 
			
		||||
    </core-shared-files-list>
 | 
			
		||||
</ion-content>
 | 
			
		||||
							
								
								
									
										63
									
								
								src/core/features/sharedfiles/pages/list/list.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/core/features/sharedfiles/pages/list/list.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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 { Component, OnInit } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreFile } from '@services/file';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { Translate } from '@singletons';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Page to display the list of shared files.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'page-core-shared-files-list',
 | 
			
		||||
    templateUrl: 'list.html',
 | 
			
		||||
})
 | 
			
		||||
export class CoreSharedFilesListPage implements OnInit {
 | 
			
		||||
 | 
			
		||||
    siteId?: string;
 | 
			
		||||
    mimetypes?: string[];
 | 
			
		||||
    manage = false;
 | 
			
		||||
    showSitePicker = false;
 | 
			
		||||
    path = '';
 | 
			
		||||
    title?: string;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        this.siteId = CoreNavigator.getRouteParam('siteId');
 | 
			
		||||
        this.mimetypes = CoreNavigator.getRouteParam('mimetypes');
 | 
			
		||||
        this.manage = !!CoreNavigator.getRouteBooleanParam('manage');
 | 
			
		||||
        this.path = CoreNavigator.getRouteParam('path') || '';
 | 
			
		||||
        this.showSitePicker = !CoreNavigator.getRouteParam('hideSitePicker');
 | 
			
		||||
 | 
			
		||||
        this.calculateTitle(this.path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Calculate the title.
 | 
			
		||||
     *
 | 
			
		||||
     * @param path Path to use.
 | 
			
		||||
     */
 | 
			
		||||
    calculateTitle(path?: string): void {
 | 
			
		||||
        if (path) {
 | 
			
		||||
            this.title = CoreFile.getFileAndDirectoryFromPath(path).name;
 | 
			
		||||
        } else {
 | 
			
		||||
            this.title = Translate.instant('core.sharedfiles.sharedfiles');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,43 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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 { CoreAppSchema } from '@services/app';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Database variables for CoreSharedFilesProvider service.
 | 
			
		||||
 */
 | 
			
		||||
export const SHARED_FILES_TABLE_NAME = 'shared_files';
 | 
			
		||||
export const APP_SCHEMA: CoreAppSchema = {
 | 
			
		||||
    name: 'CoreSharedFilesProvider',
 | 
			
		||||
    version: 1,
 | 
			
		||||
    tables: [
 | 
			
		||||
        {
 | 
			
		||||
            name: SHARED_FILES_TABLE_NAME,
 | 
			
		||||
            columns: [
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'id',
 | 
			
		||||
                    type: 'TEXT',
 | 
			
		||||
                    primaryKey: true,
 | 
			
		||||
                },
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data stored in DB for shared files.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreSharedFilesDBRecord = {
 | 
			
		||||
    id: string;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										56
									
								
								src/core/features/sharedfiles/services/handlers/settings.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/core/features/sharedfiles/services/handlers/settings.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,56 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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 { CoreSettingsHandler, CoreSettingsHandlerData } from '@features/settings/services/settings-delegate';
 | 
			
		||||
import { SHAREDFILES_PAGE_NAME } from '@features/sharedfiles/sharedfiles.module';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Shared files settings handler.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class CoreSharedFilesSettingsHandlerService implements CoreSettingsHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'CoreSharedFiles';
 | 
			
		||||
    priority = 200;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if the handler is enabled on a site level.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Whether or not the handler is enabled on a site level.
 | 
			
		||||
     */
 | 
			
		||||
    async isEnabled(): Promise<boolean> {
 | 
			
		||||
        return CoreApp.isIOS();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the data needed to render the handler.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Data needed to render the handler.
 | 
			
		||||
     */
 | 
			
		||||
    getDisplayData(): CoreSettingsHandlerData {
 | 
			
		||||
        return {
 | 
			
		||||
            icon: 'fas-folder',
 | 
			
		||||
            title: 'core.sharedfiles.sharedfiles',
 | 
			
		||||
            page: SHAREDFILES_PAGE_NAME + '/list/root',
 | 
			
		||||
            params: { manage: true, hideSitePicker: true },
 | 
			
		||||
            class: 'core-sharedfiles-settings-handler',
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const CoreSharedFilesSettingsHandler = makeSingleton(CoreSharedFilesSettingsHandlerService);
 | 
			
		||||
							
								
								
									
										74
									
								
								src/core/features/sharedfiles/services/handlers/upload.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/core/features/sharedfiles/services/handlers/upload.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,74 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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 {
 | 
			
		||||
    CoreFileUploaderHandler,
 | 
			
		||||
    CoreFileUploaderHandlerData,
 | 
			
		||||
    CoreFileUploaderHandlerResult,
 | 
			
		||||
} from '@features/fileuploader/services/fileuploader-delegate';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { CoreSharedFilesHelper } from '../sharedfiles-helper';
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to upload files from the album.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class CoreSharedFilesUploadHandlerService implements CoreFileUploaderHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'CoreSharedFilesUpload';
 | 
			
		||||
    priority = 1300;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether or not the handler is enabled on a site level.
 | 
			
		||||
     *
 | 
			
		||||
     * @return True or promise resolved with true if enabled.
 | 
			
		||||
     */
 | 
			
		||||
    async isEnabled(): Promise<boolean> {
 | 
			
		||||
        return CoreApp.isIOS();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Given a list of mimetypes, return the ones that are supported by the handler.
 | 
			
		||||
     *
 | 
			
		||||
     * @param mimetypes List of mimetypes.
 | 
			
		||||
     * @return Supported mimetypes.
 | 
			
		||||
     */
 | 
			
		||||
    getSupportedMimetypes(mimetypes: string[]): string[] {
 | 
			
		||||
        return mimetypes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the data to display the handler.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Data.
 | 
			
		||||
     */
 | 
			
		||||
    getData(): CoreFileUploaderHandlerData {
 | 
			
		||||
        return {
 | 
			
		||||
            title: 'core.sharedfiles.sharedfiles',
 | 
			
		||||
            class: 'core-sharedfiles-fileuploader-handler',
 | 
			
		||||
            icon: 'folder',
 | 
			
		||||
            action: (
 | 
			
		||||
                maxSize?: number,
 | 
			
		||||
                upload?: boolean,
 | 
			
		||||
                allowOffline?: boolean,
 | 
			
		||||
                mimetypes?: string[],
 | 
			
		||||
            ): Promise<CoreFileUploaderHandlerResult> => CoreSharedFilesHelper.pickSharedFile(mimetypes),
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const CoreSharedFilesUploadHandler = makeSingleton(CoreSharedFilesUploadHandlerService);
 | 
			
		||||
							
								
								
									
										277
									
								
								src/core/features/sharedfiles/services/sharedfiles-helper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										277
									
								
								src/core/features/sharedfiles/services/sharedfiles-helper.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,277 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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 { FileEntry } from '@ionic-native/file';
 | 
			
		||||
 | 
			
		||||
import { CoreCanceledError } from '@classes/errors/cancelederror';
 | 
			
		||||
import { CoreFileUploader } from '@features/fileuploader/services/fileuploader';
 | 
			
		||||
import { CoreFileUploaderHandlerResult } from '@features/fileuploader/services/fileuploader-delegate';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreFile } from '@services/file';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { AlertController, ApplicationInit, makeSingleton, ModalController, Platform, Translate } from '@singletons';
 | 
			
		||||
import { CoreEvents } from '@singletons/events';
 | 
			
		||||
import { CoreLogger } from '@singletons/logger';
 | 
			
		||||
import { CoreSharedFilesListModalComponent } from '../components/list-modal/list-modal';
 | 
			
		||||
import { CoreSharedFiles } from './sharedfiles';
 | 
			
		||||
import { SHAREDFILES_PAGE_NAME } from '../sharedfiles.module';
 | 
			
		||||
import { CoreSharedFilesChooseSitePage } from '../pages/choose-site/choose-site';
 | 
			
		||||
import { CoreError } from '@classes/errors/error';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Helper service to share files with the app.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class CoreSharedFilesHelperProvider {
 | 
			
		||||
 | 
			
		||||
    protected logger: CoreLogger;
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        this.logger = CoreLogger.getInstance('CoreSharedFilesHelperProvider');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Initialize.
 | 
			
		||||
     */
 | 
			
		||||
    initialize(): void {
 | 
			
		||||
        if (!CoreApp.isIOS()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let lastCheck = Date.now();
 | 
			
		||||
 | 
			
		||||
        // Check if there are new files at app start and when the app is resumed.
 | 
			
		||||
        this.searchIOSNewSharedFiles();
 | 
			
		||||
 | 
			
		||||
        Platform.resume.subscribe(() => {
 | 
			
		||||
            // Wait a bit to make sure that APP_LAUNCHED_URL is treated before this callback.
 | 
			
		||||
            setTimeout(() => {
 | 
			
		||||
                if (Date.now() - lastCheck < 1000) {
 | 
			
		||||
                    // Last check less than 1s ago, don't do anything.
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                lastCheck = Date.now();
 | 
			
		||||
                this.searchIOSNewSharedFiles();
 | 
			
		||||
            }, 200);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        CoreEvents.on(CoreEvents.APP_LAUNCHED_URL, (data) => {
 | 
			
		||||
            if (data.url.indexOf('file://') === 0) {
 | 
			
		||||
                // We received a file in iOS, it's probably a shared file. Treat it.
 | 
			
		||||
                lastCheck = Date.now();
 | 
			
		||||
                this.searchIOSNewSharedFiles(data.url);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Ask a user if he wants to replace a file (using originalName) or rename it (using newName).
 | 
			
		||||
     *
 | 
			
		||||
     * @param originalName Original name.
 | 
			
		||||
     * @param newName New name.
 | 
			
		||||
     * @return Promise resolved with the name to use when the user chooses. Rejected if user cancels.
 | 
			
		||||
     */
 | 
			
		||||
    async askRenameReplace(originalName: string, newName: string): Promise<string> {
 | 
			
		||||
        const alert = await AlertController.create({
 | 
			
		||||
            header: Translate.instant('core.sharedfiles.sharedfiles'),
 | 
			
		||||
            message: Translate.instant('core.sharedfiles.chooseactionrepeatedfile', { $a: newName }),
 | 
			
		||||
            buttons: [
 | 
			
		||||
                {
 | 
			
		||||
                    text: Translate.instant('core.sharedfiles.rename'),
 | 
			
		||||
                    role: 'rename',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    text: Translate.instant('core.sharedfiles.replace'),
 | 
			
		||||
                    role: 'replace',
 | 
			
		||||
                },
 | 
			
		||||
            ],
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await alert.present();
 | 
			
		||||
 | 
			
		||||
        const result = await alert.onDidDismiss();
 | 
			
		||||
 | 
			
		||||
        if (result.role == 'rename') {
 | 
			
		||||
            return newName;
 | 
			
		||||
        } else if (result.role == 'replace') {
 | 
			
		||||
            return originalName;
 | 
			
		||||
        } else {
 | 
			
		||||
            // Canceled.
 | 
			
		||||
            throw new CoreCanceledError();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Go to the choose site view.
 | 
			
		||||
     *
 | 
			
		||||
     * @param filePath File path to send to the view.
 | 
			
		||||
     * @param isInbox Whether the file is in the Inbox folder.
 | 
			
		||||
     */
 | 
			
		||||
    goToChooseSite(filePath: string, isInbox?: boolean): void {
 | 
			
		||||
        if (CoreSites.isLoggedIn()) {
 | 
			
		||||
            CoreNavigator.navigateToSitePath(`/${SHAREDFILES_PAGE_NAME}/choosesite`, {
 | 
			
		||||
                params: { filePath, isInbox },
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            CoreNavigator.navigate(`/${SHAREDFILES_PAGE_NAME}/choosesite`, {
 | 
			
		||||
                params: { filePath, isInbox },
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether the user is already choosing a site to store a shared file.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Whether the user is already choosing a site to store a shared file.
 | 
			
		||||
     */
 | 
			
		||||
    protected isChoosingSite(): boolean {
 | 
			
		||||
        return CoreNavigator.getCurrentRoute({ pageComponent: CoreSharedFilesChooseSitePage }) !== null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Open the view to select a shared file.
 | 
			
		||||
     *
 | 
			
		||||
     * @param mimetypes List of supported mimetypes. If undefined, all mimetypes supported.
 | 
			
		||||
     * @return Promise resolved when a file is picked, rejected if file picker is closed without selecting a file.
 | 
			
		||||
     */
 | 
			
		||||
    async pickSharedFile(mimetypes?: string[]): Promise<CoreFileUploaderHandlerResult> {
 | 
			
		||||
        const modal = await ModalController.create({
 | 
			
		||||
            component: CoreSharedFilesListModalComponent,
 | 
			
		||||
            cssClass: 'core-modal-fullscreen',
 | 
			
		||||
            componentProps: { mimetypes, pick: true },
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await modal.present();
 | 
			
		||||
 | 
			
		||||
        const result = await modal.onDidDismiss();
 | 
			
		||||
        const file: FileEntry | undefined = result.data;
 | 
			
		||||
 | 
			
		||||
        if (!file) {
 | 
			
		||||
            // User cancelled.
 | 
			
		||||
            throw new CoreCanceledError();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const error = CoreFileUploader.isInvalidMimetype(mimetypes, file.fullPath);
 | 
			
		||||
        if (error) {
 | 
			
		||||
            throw new CoreError(error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            path: file.fullPath,
 | 
			
		||||
            treated: false,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete a shared file.
 | 
			
		||||
     *
 | 
			
		||||
     * @param fileEntry The file entry to delete.
 | 
			
		||||
     * @param isInbox Whether the file is in the Inbox folder.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected removeSharedFile(fileEntry: FileEntry, isInbox?: boolean): Promise<void> {
 | 
			
		||||
        if (isInbox) {
 | 
			
		||||
            return CoreSharedFiles.deleteInboxFile(fileEntry);
 | 
			
		||||
        } else {
 | 
			
		||||
            return CoreFile.removeFileByFileEntry(fileEntry);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if there is a new file received in iOS and move it to the shared folder of current site.
 | 
			
		||||
     * If more than one site is found, the user will have to choose the site where to store it in.
 | 
			
		||||
     * If more than one file is found, treat only the first one.
 | 
			
		||||
     *
 | 
			
		||||
     * @param path Path to a file received when launching the app.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async searchIOSNewSharedFiles(path?: string): Promise<void> {
 | 
			
		||||
        try {
 | 
			
		||||
            await ApplicationInit.donePromise;
 | 
			
		||||
 | 
			
		||||
            if (this.isChoosingSite()) {
 | 
			
		||||
                // We're already treating a shared file. Abort.
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let fileEntry: FileEntry | undefined;
 | 
			
		||||
            if (path) {
 | 
			
		||||
                // The app was launched with the path to the file, get the file.
 | 
			
		||||
                fileEntry = await CoreFile.getExternalFile(path);
 | 
			
		||||
            } else {
 | 
			
		||||
                // No path received, search if there is any file in the Inbox folder.
 | 
			
		||||
                fileEntry = await CoreSharedFiles.checkIOSNewFiles();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!fileEntry) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const siteIds = await CoreSites.getSitesIds();
 | 
			
		||||
 | 
			
		||||
            if (!siteIds.length) {
 | 
			
		||||
                // No sites stored, show error and delete the file.
 | 
			
		||||
                CoreDomUtils.showErrorModal('core.sharedfiles.errorreceivefilenosites', true);
 | 
			
		||||
 | 
			
		||||
                return this.removeSharedFile(fileEntry, !path);
 | 
			
		||||
            } else if (siteIds.length == 1) {
 | 
			
		||||
                return this.storeSharedFileInSite(fileEntry, siteIds[0], !path);
 | 
			
		||||
            } else if (!this.isChoosingSite()) {
 | 
			
		||||
                this.goToChooseSite(fileEntry.toURL(), !path);
 | 
			
		||||
            }
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            if (error) {
 | 
			
		||||
                this.logger.error('Error searching iOS new shared files', error, path);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Store a shared file in a site's shared files folder.
 | 
			
		||||
     *
 | 
			
		||||
     * @param fileEntry Shared file entry.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @param isInbox Whether the file is in the Inbox folder.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async storeSharedFileInSite(fileEntry: FileEntry, siteId?: string, isInbox?: boolean): Promise<void> {
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        // First of all check if there's already a file with the same name in the shared files folder.
 | 
			
		||||
        const sharedFilesDirPath = CoreSharedFiles.getSiteSharedFilesDirPath(siteId);
 | 
			
		||||
 | 
			
		||||
        let newName = await CoreFile.getUniqueNameInFolder(sharedFilesDirPath, fileEntry.name);
 | 
			
		||||
 | 
			
		||||
        if (newName.toLowerCase() != fileEntry.name.toLowerCase()) {
 | 
			
		||||
            // Repeated name. Ask the user what he wants to do.
 | 
			
		||||
            newName = await this.askRenameReplace(fileEntry.name, newName);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await CoreSharedFiles.storeFileInSite(fileEntry, newName, siteId);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            CoreDomUtils.showErrorModal(error || 'Error moving file.');
 | 
			
		||||
        } finally {
 | 
			
		||||
            this.removeSharedFile(fileEntry, isInbox);
 | 
			
		||||
            CoreDomUtils.showAlertTranslated('core.success', 'core.sharedfiles.successstorefile');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const CoreSharedFilesHelper = makeSingleton(CoreSharedFilesHelperProvider);
 | 
			
		||||
							
								
								
									
										269
									
								
								src/core/features/sharedfiles/services/sharedfiles.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										269
									
								
								src/core/features/sharedfiles/services/sharedfiles.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,269 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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 { FileEntry, DirectoryEntry } from '@ionic-native/file';
 | 
			
		||||
import { Md5 } from 'ts-md5/dist/md5';
 | 
			
		||||
 | 
			
		||||
import { SQLiteDB } from '@classes/sqlitedb';
 | 
			
		||||
import { CoreLogger } from '@singletons/logger';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreFile } from '@services/file';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreMimetypeUtils } from '@services/utils/mimetype';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreEvents } from '@singletons/events';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { APP_SCHEMA, CoreSharedFilesDBRecord, SHARED_FILES_TABLE_NAME } from './database/sharedfiles';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service to share files with the app.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class CoreSharedFilesProvider {
 | 
			
		||||
 | 
			
		||||
    static readonly SHARED_FILES_FOLDER = 'sharedfiles';
 | 
			
		||||
 | 
			
		||||
    protected logger: CoreLogger;
 | 
			
		||||
    // Variables for DB.
 | 
			
		||||
    protected appDB: Promise<SQLiteDB>;
 | 
			
		||||
    protected resolveAppDB!: (appDB: SQLiteDB) => void;
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        this.logger = CoreLogger.getInstance('CoreSharedFilesProvider');
 | 
			
		||||
        this.appDB = new Promise(resolve => this.resolveAppDB = resolve);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Initialize database.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async initializeDatabase(): Promise<void> {
 | 
			
		||||
        try {
 | 
			
		||||
            await CoreApp.createTablesFromSchema(APP_SCHEMA);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            // Ignore errors.
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.resolveAppDB(CoreApp.getDB());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if there is a new file received in iOS. If more than one file is found, treat only the first one.
 | 
			
		||||
     * The file returned is marked as "treated" and will be deleted in the next execution.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved with a new file to be treated. If no new files found, resolved with undefined.
 | 
			
		||||
     */
 | 
			
		||||
    async checkIOSNewFiles(): Promise<FileEntry | undefined> {
 | 
			
		||||
        this.logger.debug('Search for new files on iOS');
 | 
			
		||||
 | 
			
		||||
        const entries = await CoreUtils.ignoreErrors(CoreFile.getDirectoryContents('Inbox'));
 | 
			
		||||
 | 
			
		||||
        if (!entries || !entries.length) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let fileToReturn: FileEntry | undefined;
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < entries.length; i++) {
 | 
			
		||||
            if (entries[i].isDirectory) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const fileEntry = <FileEntry> entries[i];
 | 
			
		||||
            const fileId = this.getFileId(fileEntry);
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                // Check if file was already treated.
 | 
			
		||||
                await this.isFileTreated(fileId);
 | 
			
		||||
 | 
			
		||||
                // File already treated, delete it. No need to block the execution for this.
 | 
			
		||||
                this.deleteInboxFile(fileEntry);
 | 
			
		||||
            } catch {
 | 
			
		||||
                // File not treated before.
 | 
			
		||||
                this.logger.debug(`Found new file ${fileEntry.name} shared with the app.`);
 | 
			
		||||
                fileToReturn = fileEntry;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!fileToReturn) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Mark it as "treated".
 | 
			
		||||
        const fileId = this.getFileId(fileToReturn);
 | 
			
		||||
 | 
			
		||||
        await this.markAsTreated(fileId);
 | 
			
		||||
 | 
			
		||||
        this.logger.debug(`File marked as "treated": ${fileToReturn.name}`);
 | 
			
		||||
 | 
			
		||||
        return fileToReturn;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Deletes a file in the Inbox folder (shared with the app).
 | 
			
		||||
     *
 | 
			
		||||
     * @param entry FileEntry.
 | 
			
		||||
     * @return Promise resolved when done, rejected otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    async deleteInboxFile(entry: FileEntry): Promise<void> {
 | 
			
		||||
        this.logger.debug('Delete inbox file: ' + entry.name);
 | 
			
		||||
 | 
			
		||||
        await CoreUtils.ignoreErrors(CoreFile.removeFileByFileEntry(entry));
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await this.unmarkAsTreated(this.getFileId(entry));
 | 
			
		||||
 | 
			
		||||
            this.logger.debug(`"Treated" mark removed from file: ${entry.name}`);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            this.logger.debug(`Error deleting "treated" mark from file: ${entry.name}`, error);
 | 
			
		||||
 | 
			
		||||
            throw error;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the ID of a file for managing "treated" files.
 | 
			
		||||
     *
 | 
			
		||||
     * @param entry FileEntry.
 | 
			
		||||
     * @return File ID.
 | 
			
		||||
     */
 | 
			
		||||
    protected getFileId(entry: FileEntry): string {
 | 
			
		||||
        return <string> Md5.hashAsciiStr(entry.name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the shared files stored in a site.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @param path Path to search inside the site shared folder.
 | 
			
		||||
     * @param mimetypes List of supported mimetypes. If undefined, all mimetypes supported.
 | 
			
		||||
     * @return Promise resolved with the files.
 | 
			
		||||
     */
 | 
			
		||||
    async getSiteSharedFiles(siteId?: string, path?: string, mimetypes?: string[]): Promise<(FileEntry | DirectoryEntry)[]> {
 | 
			
		||||
        let pathToGet = this.getSiteSharedFilesDirPath(siteId);
 | 
			
		||||
        if (path) {
 | 
			
		||||
            pathToGet = CoreTextUtils.concatenatePaths(pathToGet, path);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            let entries = await CoreFile.getDirectoryContents(pathToGet);
 | 
			
		||||
 | 
			
		||||
            if (mimetypes) {
 | 
			
		||||
                // Get only files with the right mimetype and the ones we cannot determine the mimetype.
 | 
			
		||||
                entries = entries.filter((entry) => {
 | 
			
		||||
                    const extension = CoreMimetypeUtils.getFileExtension(entry.name);
 | 
			
		||||
                    const mimetype = CoreMimetypeUtils.getMimeType(extension);
 | 
			
		||||
 | 
			
		||||
                    return !mimetype || mimetypes.indexOf(mimetype) > -1;
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return entries;
 | 
			
		||||
        } catch {
 | 
			
		||||
            // Directory not found, return empty list.
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the path to a site's shared files folder.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Path.
 | 
			
		||||
     */
 | 
			
		||||
    getSiteSharedFilesDirPath(siteId?: string): string {
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        return CoreFile.getSiteFolder(siteId) + '/' + CoreSharedFilesProvider.SHARED_FILES_FOLDER;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if a file has been treated already.
 | 
			
		||||
     *
 | 
			
		||||
     * @param fileId File ID.
 | 
			
		||||
     * @return Resolved if treated, rejected otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    protected async isFileTreated(fileId: string): Promise<CoreSharedFilesDBRecord> {
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
 | 
			
		||||
        return db.getRecord(SHARED_FILES_TABLE_NAME, { id: fileId });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Mark a file as treated.
 | 
			
		||||
     *
 | 
			
		||||
     * @param fileId File ID.
 | 
			
		||||
     * @return Promise resolved when marked.
 | 
			
		||||
     */
 | 
			
		||||
    protected async markAsTreated(fileId: string): Promise<void> {
 | 
			
		||||
        try {
 | 
			
		||||
            // Check if it's already marked.
 | 
			
		||||
            await this.isFileTreated(fileId);
 | 
			
		||||
        } catch (err) {
 | 
			
		||||
            // Doesn't exist, insert it.
 | 
			
		||||
            const db = await this.appDB;
 | 
			
		||||
 | 
			
		||||
            await db.insertRecord(SHARED_FILES_TABLE_NAME, { id: fileId });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Store a file in a site's shared folder.
 | 
			
		||||
     *
 | 
			
		||||
     * @param entry File entry.
 | 
			
		||||
     * @param newName Name of the new file. If not defined, use original file's name.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async storeFileInSite(entry: FileEntry, newName?: string, siteId?: string): Promise<FileEntry | undefined> {
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        if (!entry || !siteId) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        newName = newName || entry.name;
 | 
			
		||||
 | 
			
		||||
        const sharedFilesFolder = this.getSiteSharedFilesDirPath(siteId);
 | 
			
		||||
        const newPath = CoreTextUtils.concatenatePaths(sharedFilesFolder, newName);
 | 
			
		||||
 | 
			
		||||
        // Create dir if it doesn't exist already.
 | 
			
		||||
        await CoreFile.createDir(sharedFilesFolder);
 | 
			
		||||
 | 
			
		||||
        const newFile = await CoreFile.moveExternalFile(entry.toURL(), newPath);
 | 
			
		||||
 | 
			
		||||
        CoreEvents.trigger(CoreEvents.FILE_SHARED, { siteId, name: newName });
 | 
			
		||||
 | 
			
		||||
        return newFile;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Unmark a file as treated.
 | 
			
		||||
     *
 | 
			
		||||
     * @param fileId File ID.
 | 
			
		||||
     * @return Resolved when unmarked.
 | 
			
		||||
     */
 | 
			
		||||
    protected async unmarkAsTreated(fileId: string): Promise<void> {
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
 | 
			
		||||
        await db.deleteRecords(SHARED_FILES_TABLE_NAME, { id: fileId });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const CoreSharedFiles = makeSingleton(CoreSharedFilesProvider);
 | 
			
		||||
							
								
								
									
										45
									
								
								src/core/features/sharedfiles/sharedfiles-lazy.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/core/features/sharedfiles/sharedfiles-lazy.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,45 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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 { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreSharedFilesComponentsModule } from './components/components.module';
 | 
			
		||||
import { CoreSharedFilesListPage } from './pages/list/list';
 | 
			
		||||
import { CoreSharedFilesChooseSitePage } from './pages/choose-site/choose-site';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
    {
 | 
			
		||||
        path: 'choosesite',
 | 
			
		||||
        component: CoreSharedFilesChooseSitePage,
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        path: 'list/:hash',
 | 
			
		||||
        component: CoreSharedFilesListPage,
 | 
			
		||||
    },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
        RouterModule.forChild(routes),
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        CoreSharedFilesComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        CoreSharedFilesListPage,
 | 
			
		||||
        CoreSharedFilesChooseSitePage,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class CoreSharedFilesLazyModule {}
 | 
			
		||||
							
								
								
									
										64
									
								
								src/core/features/sharedfiles/sharedfiles.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/core/features/sharedfiles/sharedfiles.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,64 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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 { AppRoutingModule } from '@/app/app-routing.module';
 | 
			
		||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
 | 
			
		||||
import { Routes } from '@angular/router';
 | 
			
		||||
 | 
			
		||||
import { CoreFileUploaderDelegate } from '@features/fileuploader/services/fileuploader-delegate';
 | 
			
		||||
import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
 | 
			
		||||
import { CoreSitePreferencesRoutingModule } from '@features/settings/pages/site/site-routing';
 | 
			
		||||
import { CoreSettingsDelegate } from '@features/settings/services/settings-delegate';
 | 
			
		||||
import { CoreSharedFilesComponentsModule } from './components/components.module';
 | 
			
		||||
import { CoreSharedFilesSettingsHandler } from './services/handlers/settings';
 | 
			
		||||
import { CoreSharedFilesUploadHandler } from './services/handlers/upload';
 | 
			
		||||
import { CoreSharedFiles, CoreSharedFilesProvider } from './services/sharedfiles';
 | 
			
		||||
import { CoreSharedFilesHelper, CoreSharedFilesHelperProvider } from './services/sharedfiles-helper';
 | 
			
		||||
 | 
			
		||||
export const CORE_SHAREDFILES_SERVICES: Type<unknown>[] = [
 | 
			
		||||
    CoreSharedFilesProvider,
 | 
			
		||||
    CoreSharedFilesHelperProvider,
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export const SHAREDFILES_PAGE_NAME = 'sharedfiles';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
    {
 | 
			
		||||
        path: SHAREDFILES_PAGE_NAME,
 | 
			
		||||
        loadChildren: () => import('./sharedfiles-lazy.module').then(m => m.CoreSharedFilesLazyModule),
 | 
			
		||||
    },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
        AppRoutingModule.forChild(routes),
 | 
			
		||||
        CoreMainMenuTabRoutingModule.forChild(routes),
 | 
			
		||||
        CoreSitePreferencesRoutingModule.forChild(routes),
 | 
			
		||||
        CoreSharedFilesComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
        {
 | 
			
		||||
            provide: APP_INITIALIZER,
 | 
			
		||||
            multi: true,
 | 
			
		||||
            useFactory: () => async () => {
 | 
			
		||||
                CoreFileUploaderDelegate.registerHandler(CoreSharedFilesUploadHandler.instance);
 | 
			
		||||
                CoreSettingsDelegate.registerHandler(CoreSharedFilesSettingsHandler.instance);
 | 
			
		||||
 | 
			
		||||
                CoreSharedFilesHelper.initialize();
 | 
			
		||||
                await CoreSharedFiles.initializeDatabase();
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class CoreSharedFilesModule {}
 | 
			
		||||
@ -465,6 +465,21 @@ export class CoreNavigatorService {
 | 
			
		||||
        return 'param-' + (++this.lastParamId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Replace the route params in a path with the params values.
 | 
			
		||||
     *
 | 
			
		||||
     * @param path Path.
 | 
			
		||||
     * @param params Params.
 | 
			
		||||
     * @returns Path with params replaced.
 | 
			
		||||
     */
 | 
			
		||||
    replaceRoutePathParams(path: string, params?: Params): string {
 | 
			
		||||
        for (const name in params) {
 | 
			
		||||
            path = path.replace(`:${name}`, params[name]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return path;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const CoreNavigator = makeSingleton(CoreNavigatorService);
 | 
			
		||||
 | 
			
		||||
@ -51,6 +51,8 @@ export interface CoreEventsData {
 | 
			
		||||
    [CoreEvents.LOGIN_SITE_CHECKED]: CoreEventLoginSiteCheckedData;
 | 
			
		||||
    [CoreEvents.SEND_ON_ENTER_CHANGED]: CoreEventSendOnEnterChangedData;
 | 
			
		||||
    [CoreEvents.COMPONENT_FILE_ACTION]: CoreFilepoolComponentFileEventData;
 | 
			
		||||
    [CoreEvents.FILE_SHARED]: CoreEventFileSharedData;
 | 
			
		||||
    [CoreEvents.APP_LAUNCHED_URL]: CoreEventAppLaunchedData;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@ -359,3 +361,18 @@ export type CoreEventLoginSiteCheckedData = {
 | 
			
		||||
export type CoreEventSendOnEnterChangedData = {
 | 
			
		||||
    sendOnEnter: boolean;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data passed to FILE_SHARED event.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreEventFileSharedData = {
 | 
			
		||||
    name: string;
 | 
			
		||||
    siteId: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data passed to APP_LAUNCHED_URL event.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreEventAppLaunchedData = {
 | 
			
		||||
    url: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user