diff --git a/scripts/langindex.json b/scripts/langindex.json
index e8a39aa49..a20b51ddd 100644
--- a/scripts/langindex.json
+++ b/scripts/langindex.json
@@ -24,6 +24,8 @@
"addon.block_myoverview.title": "block_myoverview",
"addon.block_recentlyaccessedcourses.nocourses": "block_recentlyaccessedcourses",
"addon.block_recentlyaccessedcourses.pluginname": "block_recentlyaccessedcourses",
+ "addon.block_recentlyaccesseditems.noitems": "block_recentlyaccesseditems",
+ "addon.block_recentlyaccesseditems.pluginname": "block_recentlyaccesseditems",
"addon.block_sitemainmenu.pluginname": "block_site_main_menu",
"addon.block_timeline.duedate": "block_timeline",
"addon.block_timeline.next30days": "block_timeline",
diff --git a/src/addon/block/recentlyaccesseditems/components/components.module.ts b/src/addon/block/recentlyaccesseditems/components/components.module.ts
new file mode 100644
index 000000000..1ce6d5557
--- /dev/null
+++ b/src/addon/block/recentlyaccesseditems/components/components.module.ts
@@ -0,0 +1,45 @@
+// (C) Copyright 2015 Martin Dougiamas
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { IonicModule } from 'ionic-angular';
+import { TranslateModule } from '@ngx-translate/core';
+import { AddonBlockRecentlyAccessedItemsComponent } from './recentlyaccesseditems/recentlyaccesseditems';
+import { CoreComponentsModule } from '@components/components.module';
+import { CoreDirectivesModule } from '@directives/directives.module';
+import { CoreCourseComponentsModule } from '@core/course/components/components.module';
+
+@NgModule({
+ declarations: [
+ AddonBlockRecentlyAccessedItemsComponent
+ ],
+ imports: [
+ CommonModule,
+ IonicModule,
+ TranslateModule.forChild(),
+ CoreComponentsModule,
+ CoreDirectivesModule,
+ CoreCourseComponentsModule
+ ],
+ providers: [
+ ],
+ exports: [
+ AddonBlockRecentlyAccessedItemsComponent
+ ],
+ entryComponents: [
+ AddonBlockRecentlyAccessedItemsComponent
+ ]
+})
+export class AddonBlockRecentlyAccessedItemsComponentsModule {}
diff --git a/src/addon/block/recentlyaccesseditems/components/recentlyaccesseditems/addon-block-recentlyaccesseditems.html b/src/addon/block/recentlyaccesseditems/components/recentlyaccesseditems/addon-block-recentlyaccesseditems.html
new file mode 100644
index 000000000..5b514dc86
--- /dev/null
+++ b/src/addon/block/recentlyaccesseditems/components/recentlyaccesseditems/addon-block-recentlyaccesseditems.html
@@ -0,0 +1,15 @@
+
+ {{ 'addon.block_recentlyaccesseditems.pluginname' | translate }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/addon/block/recentlyaccesseditems/components/recentlyaccesseditems/recentlyaccesseditems.ts b/src/addon/block/recentlyaccesseditems/components/recentlyaccesseditems/recentlyaccesseditems.ts
new file mode 100644
index 000000000..e1c9bf372
--- /dev/null
+++ b/src/addon/block/recentlyaccesseditems/components/recentlyaccesseditems/recentlyaccesseditems.ts
@@ -0,0 +1,90 @@
+// (C) Copyright 2015 Martin Dougiamas
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { Component, OnInit, Injector, Optional } from '@angular/core';
+import { NavController } from 'ionic-angular';
+import { CoreSitesProvider } from '@providers/sites';
+import { CoreBlockBaseComponent } from '@core/block/classes/base-block-component';
+import { AddonBlockRecentlyAccessedItemsProvider } from '../../providers/recentlyaccesseditems';
+import { CoreTextUtilsProvider } from '@providers/utils/text';
+import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
+
+/**
+ * Component to render a recently accessed items block.
+ */
+@Component({
+ selector: 'addon-block-recentlyaccesseditems',
+ templateUrl: 'addon-block-recentlyaccesseditems.html'
+})
+export class AddonBlockRecentlyAccessedItemsComponent extends CoreBlockBaseComponent implements OnInit {
+ items = [];
+
+ protected fetchContentDefaultError = 'Error getting recently accessed items data.';
+
+ constructor(injector: Injector, @Optional() private navCtrl: NavController,
+ private sitesProvider: CoreSitesProvider, private textUtils: CoreTextUtilsProvider,
+ private recentItemsProvider: AddonBlockRecentlyAccessedItemsProvider,
+ private contentLinksHelper: CoreContentLinksHelperProvider) {
+
+ super(injector, 'AddonBlockRecentlyAccessedItemsComponent');
+ }
+
+ /**
+ * Component being initialized.
+ */
+ ngOnInit(): void {
+ super.ngOnInit();
+ }
+
+ /**
+ * Perform the invalidate content function.
+ *
+ * @return {Promise} Resolved when done.
+ */
+ protected invalidateContent(): Promise {
+ return this.recentItemsProvider.invalidateRecentItems();
+ }
+
+ /**
+ * Fetch the data to render the block.
+ *
+ * @return {Promise} Promise resolved when done.
+ */
+ protected fetchContent(): Promise {
+ return this.recentItemsProvider.getRecentItems().then((items) => {
+ this.items = items;
+ });
+ }
+
+ /**
+ * Event clicked.
+ *
+ * @param {Event} e Click event.
+ * @param {any} item Activity item info.
+ */
+ action(e: Event, item: any): void {
+ e.preventDefault();
+ e.stopPropagation();
+
+ const url = this.textUtils.decodeHTMLEntities(item.viewurl);
+ const modal = this.domUtils.showModalLoading();
+ this.contentLinksHelper.handleLink(url, undefined, this.navCtrl).then((treated) => {
+ if (!treated) {
+ return this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(url);
+ }
+ }).finally(() => {
+ modal.dismiss();
+ });
+ }
+}
diff --git a/src/addon/block/recentlyaccesseditems/lang/en.json b/src/addon/block/recentlyaccesseditems/lang/en.json
new file mode 100644
index 000000000..47311d281
--- /dev/null
+++ b/src/addon/block/recentlyaccesseditems/lang/en.json
@@ -0,0 +1,4 @@
+{
+ "noitems": "No recent items",
+ "pluginname": "Recently accessed items"
+}
diff --git a/src/addon/block/recentlyaccesseditems/providers/block-handler.ts b/src/addon/block/recentlyaccesseditems/providers/block-handler.ts
new file mode 100644
index 000000000..2963017e8
--- /dev/null
+++ b/src/addon/block/recentlyaccesseditems/providers/block-handler.ts
@@ -0,0 +1,50 @@
+// (C) Copyright 2015 Martin Dougiamas
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { Injectable, Injector } from '@angular/core';
+import { CoreBlockHandlerData } from '@core/block/providers/delegate';
+import { AddonBlockRecentlyAccessedItemsComponent } from '../components/recentlyaccesseditems/recentlyaccesseditems';
+import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler';
+
+/**
+ * Block handler.
+ */
+@Injectable()
+export class AddonBlockRecentlyAccessedItemsHandler extends CoreBlockBaseHandler {
+ name = 'AddonBlockRecentlyAccessedItems';
+ blockName = 'recentlyaccesseditems';
+
+ constructor() {
+ super();
+ }
+
+ /**
+ * Returns the data needed to render the block.
+ *
+ * @param {Injector} injector Injector.
+ * @param {any} block The block to render.
+ * @param {string} contextLevel The context where the block will be used.
+ * @param {number} instanceId The instance ID associated with the context level.
+ * @return {CoreBlockHandlerData|Promise} Data or promise resolved with the data.
+ */
+ getDisplayData?(injector: Injector, block: any, contextLevel: string, instanceId: number)
+ : CoreBlockHandlerData | Promise {
+
+ return {
+ title: 'addon.block_recentlyaccesseditems.pluginname',
+ class: 'addon-block-recentlyaccesseditems',
+ component: AddonBlockRecentlyAccessedItemsComponent
+ };
+ }
+}
diff --git a/src/addon/block/recentlyaccesseditems/providers/recentlyaccesseditems.ts b/src/addon/block/recentlyaccesseditems/providers/recentlyaccesseditems.ts
new file mode 100644
index 000000000..e20e41b1f
--- /dev/null
+++ b/src/addon/block/recentlyaccesseditems/providers/recentlyaccesseditems.ts
@@ -0,0 +1,71 @@
+// (C) Copyright 2015 Martin Dougiamas
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { Injectable } from '@angular/core';
+import { CoreSitesProvider } from '@providers/sites';
+import { CoreCourseProvider } from '@core/course/providers/course';
+
+/**
+ * Service that provides some features regarding recently accessed items.
+ */
+@Injectable()
+export class AddonBlockRecentlyAccessedItemsProvider {
+ protected ROOT_CACHE_KEY = 'AddonBlockRecentlyAccessedItems:';
+
+ constructor(private sitesProvider: CoreSitesProvider, private courseProvider: CoreCourseProvider) { }
+
+ /**
+ * Get cache key for get last accessed items value WS call.
+ *
+ * @return {string} Cache key.
+ */
+ protected getRecentItemsCacheKey(): string {
+ return this.ROOT_CACHE_KEY + ':recentitems';
+ }
+
+ /**
+ * Get last accessed items.
+ *
+ * @param {string} [siteId] Site ID. If not defined, use current site.
+ * @return {Promise} Promise resolved when the info is retrieved.
+ */
+ getRecentItems(siteId?: string): Promise {
+
+ return this.sitesProvider.getSite(siteId).then((site) => {
+ const preSets = {
+ cacheKey: this.getRecentItemsCacheKey()
+ };
+
+ return site.read('block_recentlyaccesseditems_get_recent_items', undefined, preSets).then((items) => {
+ return items.map((item) => {
+ item.iconUrl = this.courseProvider.getModuleIconSrc(item.modname);
+
+ return item;
+ });
+ });
+ });
+ }
+
+ /**
+ * Invalidates get last accessed items WS call.
+ *
+ * @param {string} [siteId] Site ID to invalidate. If not defined, use current site.
+ * @return {Promise} Promise resolved when the data is invalidated.
+ */
+ invalidateRecentItems(siteId?: string): Promise {
+ return this.sitesProvider.getSite(siteId).then((site) => {
+ return site.invalidateWsCacheForKey(this.getRecentItemsCacheKey());
+ });
+ }
+}
diff --git a/src/addon/block/recentlyaccesseditems/recentlyaccesseditems.module.ts b/src/addon/block/recentlyaccesseditems/recentlyaccesseditems.module.ts
new file mode 100644
index 000000000..5df77fe5f
--- /dev/null
+++ b/src/addon/block/recentlyaccesseditems/recentlyaccesseditems.module.ts
@@ -0,0 +1,42 @@
+// (C) Copyright 2015 Martin Dougiamas
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { NgModule } from '@angular/core';
+import { IonicModule } from 'ionic-angular';
+import { TranslateModule } from '@ngx-translate/core';
+import { AddonBlockRecentlyAccessedItemsComponentsModule } from './components/components.module';
+import { CoreBlockDelegate } from '@core/block/providers/delegate';
+import { AddonBlockRecentlyAccessedItemsHandler } from './providers/block-handler';
+import { AddonBlockRecentlyAccessedItemsProvider } from './providers/recentlyaccesseditems';
+
+@NgModule({
+ declarations: [
+ ],
+ imports: [
+ IonicModule,
+ AddonBlockRecentlyAccessedItemsComponentsModule,
+ TranslateModule.forChild()
+ ],
+ exports: [
+ ],
+ providers: [
+ AddonBlockRecentlyAccessedItemsHandler,
+ AddonBlockRecentlyAccessedItemsProvider
+ ]
+})
+export class AddonBlockRecentlyAccessedItemsModule {
+ constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockRecentlyAccessedItemsHandler) {
+ blockDelegate.registerHandler(blockHandler);
+ }
+}
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index d54c8dfe3..ca4cd023b 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -89,6 +89,7 @@ import { AddonBlockMyOverviewModule } from '@addon/block/myoverview/myoverview.m
import { AddonBlockSiteMainMenuModule } from '@addon/block/sitemainmenu/sitemainmenu.module';
import { AddonBlockTimelineModule } from '@addon/block/timeline/timeline.module';
import { AddonBlockRecentlyAccessedCoursesModule } from '@addon/block/recentlyaccessedcourses/recentlyaccessedcourses.module';
+import { AddonBlockRecentlyAccessedItemsModule } from '@addon/block/recentlyaccesseditems/recentlyaccesseditems.module';
import { AddonModAssignModule } from '@addon/mod/assign/assign.module';
import { AddonModBookModule } from '@addon/mod/book/book.module';
import { AddonModChatModule } from '@addon/mod/chat/chat.module';
@@ -204,6 +205,7 @@ export const CORE_PROVIDERS: any[] = [
AddonBlockSiteMainMenuModule,
AddonBlockTimelineModule,
AddonBlockRecentlyAccessedCoursesModule,
+ AddonBlockRecentlyAccessedItemsModule,
AddonModAssignModule,
AddonModBookModule,
AddonModChatModule,
diff --git a/src/assets/lang/en.json b/src/assets/lang/en.json
index ba1ffd309..7de748385 100644
--- a/src/assets/lang/en.json
+++ b/src/assets/lang/en.json
@@ -24,6 +24,8 @@
"addon.block_myoverview.title": "Title",
"addon.block_recentlyaccessedcourses.nocourses": "No recent courses",
"addon.block_recentlyaccessedcourses.pluginname": "Recently accessed courses",
+ "addon.block_recentlyaccesseditems.noitems": "No recent items",
+ "addon.block_recentlyaccesseditems.pluginname": "Recently accessed items",
"addon.block_sitemainmenu.pluginname": "Main menu",
"addon.block_timeline.duedate": "Due date",
"addon.block_timeline.next30days": "Next 30 days",
@@ -1647,4 +1649,4 @@
"core.year": "year",
"core.years": "years",
"core.yes": "Yes"
-}
\ No newline at end of file
+}