commit
1a6f6a0e3e
|
@ -15,8 +15,7 @@ cache:
|
|||
- $HOME/.cache/electron-builder
|
||||
|
||||
before_script:
|
||||
- npm install -g @angular/cli
|
||||
- npm i npm@latest -g
|
||||
- npm install npm@latest -g
|
||||
- gulp
|
||||
|
||||
script:
|
||||
|
|
12
Dockerfile
12
Dockerfile
|
@ -11,17 +11,17 @@ EXPOSE 35729
|
|||
# Port 53703 is the Chrome dev logger port.
|
||||
EXPOSE 53703
|
||||
|
||||
# MoodleMobile uses Ionic and Gulp.
|
||||
RUN npm i -g ionic gulp && rm -rf /root/.npm
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . /app
|
||||
|
||||
# Install npm libraries and run gulp to initialize the project.
|
||||
RUN npm install && gulp && rm -rf /root/.npm
|
||||
# Install npm libraries.
|
||||
RUN npm install && rm -rf /root/.npm
|
||||
|
||||
# Run gulp before starting.
|
||||
RUN npx gulp
|
||||
|
||||
# Provide a Healthcheck command for easier use in CI.
|
||||
HEALTHCHECK --interval=10s --timeout=3s --start-period=30s CMD curl -f http://localhost:8100 || exit 1
|
||||
|
||||
CMD ["ionic", "serve", "-b"]
|
||||
CMD ["npm", "run", "ionic:serve"]
|
||||
|
|
33
config.xml
33
config.xml
|
@ -1,5 +1,5 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<widget id="com.moodle.moodlemobile" version="3.7.0" xmlns="http://www.w3.org/ns/widgets" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
||||
<widget id="com.moodle.moodlemobile" version="3.7.1" xmlns="http://www.w3.org/ns/widgets" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
||||
<name>Moodle</name>
|
||||
<description>Moodle official app</description>
|
||||
<author email="mobile@moodle.com" href="http://moodle.com">Moodle Mobile team</author>
|
||||
|
@ -23,7 +23,7 @@
|
|||
<preference name="webviewbounce" value="false" />
|
||||
<preference name="AppendUserAgent" value="MoodleMobile" />
|
||||
<preference name="android-minSdkVersion" value="19" />
|
||||
<preference name="android-targetSdkVersion" value="26" />
|
||||
<preference name="android-targetSdkVersion" value="28" />
|
||||
<preference name="UIWebViewBounce" value="false" />
|
||||
<preference name="DisallowOverscroll" value="true" />
|
||||
<preference name="BackupWebStorage" value="none" />
|
||||
|
@ -113,30 +113,30 @@
|
|||
<icon height="1024" src="resources/ios/icon/icon-1024.png" width="1024" />
|
||||
<splash height="2732" src="resources/ios/splash/Default@2x~universal~anyany.png" width="2732" />
|
||||
</platform>
|
||||
<plugin name="com-darryncampbell-cordova-plugin-intent" spec="1.1.7" />
|
||||
<plugin name="cordova-android-support-gradle-release" spec="3.0.0">
|
||||
<plugin name="com-darryncampbell-cordova-plugin-intent" spec="1.1.8" />
|
||||
<plugin name="cordova-android-support-gradle-release" spec="3.0.1">
|
||||
<variable name="ANDROID_SUPPORT_VERSION" value="27.1.0" />
|
||||
</plugin>
|
||||
<plugin name="cordova-clipboard" spec="1.2.1" />
|
||||
<plugin name="cordova-clipboard" spec="1.3.0" />
|
||||
<plugin name="cordova-plugin-badge" spec="0.8.8" />
|
||||
<plugin name="cordova-plugin-camera" spec="4.0.3" />
|
||||
<plugin name="cordova-plugin-camera" spec="4.1.0" />
|
||||
<plugin name="cordova-plugin-customurlscheme" spec="4.4.0">
|
||||
<variable name="URL_SCHEME" value="moodlemobile" />
|
||||
</plugin>
|
||||
<plugin name="cordova-plugin-device" spec="2.0.2" />
|
||||
<plugin name="cordova-plugin-file" spec="6.0.1" />
|
||||
<plugin name="cordova-plugin-device" spec="2.0.3" />
|
||||
<plugin name="cordova-plugin-file" spec="6.0.2" />
|
||||
<plugin name="cordova-plugin-file-opener2" spec="2.0.19" />
|
||||
<plugin name="cordova-plugin-file-transfer" spec="1.7.1" />
|
||||
<plugin name="cordova-plugin-globalization" spec="1.11.0" />
|
||||
<plugin name="cordova-plugin-inappbrowser" spec="3.0.0" />
|
||||
<plugin name="cordova-plugin-inappbrowser" spec="3.1.0" />
|
||||
<plugin name="cordova-plugin-ionic-keyboard" spec="2.1.3" />
|
||||
<plugin name="cordova-plugin-local-notification" spec="https://github.com/moodlemobile/cordova-plugin-local-notification.git#moodle" />
|
||||
<plugin name="cordova-plugin-media-capture" spec="3.0.2" />
|
||||
<plugin name="cordova-plugin-network-information" spec="2.0.1" />
|
||||
<plugin name="cordova-plugin-screen-orientation" spec="3.0.1" />
|
||||
<plugin name="cordova-plugin-splashscreen" spec="5.0.2" />
|
||||
<plugin name="cordova-plugin-statusbar" spec="2.4.2" />
|
||||
<plugin name="cordova-plugin-whitelist" spec="1.3.3" />
|
||||
<plugin name="cordova-plugin-media-capture" spec="3.0.3" />
|
||||
<plugin name="cordova-plugin-network-information" spec="2.0.2" />
|
||||
<plugin name="cordova-plugin-screen-orientation" spec="3.0.2" />
|
||||
<plugin name="cordova-plugin-splashscreen" spec="5.0.3" />
|
||||
<plugin name="cordova-plugin-statusbar" spec="2.4.3" />
|
||||
<plugin name="cordova-plugin-whitelist" spec="1.3.4" />
|
||||
<plugin name="cordova-plugin-zip" spec="3.1.0" />
|
||||
<plugin name="cordova-sqlite-storage" spec="2.6.0" />
|
||||
<plugin name="nl.kingsquare.cordova.background-audio" spec="1.0.1" />
|
||||
|
@ -147,6 +147,9 @@
|
|||
<edit-config file="AndroidManifest.xml" mode="merge" target="/manifest/application/activity[@android:name='MainActivity']">
|
||||
<activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|screenLayout|smallestScreenSize" android:debuggable="true" />
|
||||
</edit-config>
|
||||
<edit-config file="AndroidManifest.xml" mode="merge" target="/manifest/application">
|
||||
<application android:usesCleartextTraffic="true" />
|
||||
</edit-config>
|
||||
<config-file parent="/manifest/application" target="AndroidManifest.xml">
|
||||
<meta-data android:name="firebase_analytics_collection_deactivated" android:value="true" />
|
||||
</config-file>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<Identity Name="3312ADB7.MoodleDesktop"
|
||||
ProcessorArchitecture="x64"
|
||||
Publisher="CN=33CDCDF6-1EB5-4827-9897-ED25C91A32F6"
|
||||
Version="3.7.0.0" />
|
||||
Version="3.7.1.0" />
|
||||
<Properties>
|
||||
<DisplayName>Moodle Desktop</DisplayName>
|
||||
<PublisherDisplayName>Moodle Pty Ltd.</PublisherDisplayName>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
62
package.json
62
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "moodlemobile",
|
||||
"version": "3.7.0",
|
||||
"version": "3.7.1",
|
||||
"description": "The official app for Moodle.",
|
||||
"author": {
|
||||
"name": "Moodle Pty Ltd.",
|
||||
|
@ -23,21 +23,21 @@
|
|||
}
|
||||
],
|
||||
"scripts": {
|
||||
"setup": "npm install && cordova prepare && gulp",
|
||||
"clean": "ionic-app-scripts clean",
|
||||
"build": "ionic-app-scripts build",
|
||||
"lint": "ionic-app-scripts lint",
|
||||
"setup": "npm install && npx cordova prepare && npx gulp",
|
||||
"clean": "npx ionic-app-scripts clean",
|
||||
"build": "npx ionic-app-scripts build",
|
||||
"lint": "npx ionic-app-scripts lint",
|
||||
"ionic:build": "node --max-old-space-size=16384 ./node_modules/@ionic/app-scripts/bin/ionic-app-scripts.js build",
|
||||
"ionic:serve:before": "gulp",
|
||||
"ionic:serve": "gulp watch | ionic-app-scripts serve",
|
||||
"ionic:build:before": "gulp",
|
||||
"ionic:watch:before": "gulp",
|
||||
"ionic:build:after": "gulp copy-component-templates",
|
||||
"preionic:build": "gulp",
|
||||
"postionic:build": "gulp copy-component-templates",
|
||||
"desktop.pack": "electron-builder --dir",
|
||||
"desktop.dist": "electron-builder -p never",
|
||||
"windows.store": "electron-windows-store --input-directory .\\desktop\\dist\\win-unpacked --output-directory .\\desktop\\store --flatten true -a .\\resources\\desktop -m .\\desktop\\assets\\windows\\AppXManifest.xml --package-version 0.0.0.0 --package-name MoodleDesktop"
|
||||
"ionic:serve:before": "npx gulp",
|
||||
"ionic:serve": "npx gulp watch & npx ionic-app-scripts serve -b --devapp --address=0.0.0.0",
|
||||
"ionic:build:before": "npx gulp",
|
||||
"ionic:watch:before": "npx gulp",
|
||||
"ionic:build:after": "npx gulp copy-component-templates",
|
||||
"preionic:build": "npx gulp",
|
||||
"postionic:build": "npx gulp copy-component-templates",
|
||||
"desktop.pack": "npx electron-builder --dir",
|
||||
"desktop.dist": "npx electron-builder -p never",
|
||||
"windows.store": "npx electron-windows-store --input-directory .\\desktop\\dist\\win-unpacked --output-directory .\\desktop\\store -a .\\resources\\desktop -m .\\desktop\\assets\\windows\\AppXManifest.xml --package-version 0.0.0.0 --package-name MoodleDesktop"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/animations": "5.2.10",
|
||||
|
@ -80,27 +80,28 @@
|
|||
"@types/promise.prototype.finally": "2.0.2",
|
||||
"chart.js": "2.7.2",
|
||||
"com-darryncampbell-cordova-plugin-intent": "1.1.7",
|
||||
"cordova": "8.1.2",
|
||||
"cordova-android": "7.1.2",
|
||||
"cordova-android-support-gradle-release": "3.0.0",
|
||||
"cordova-clipboard": "1.2.1",
|
||||
"cordova-android-support-gradle-release": "3.0.1",
|
||||
"cordova-clipboard": "1.3.0",
|
||||
"cordova-ios": "4.5.5",
|
||||
"cordova-plugin-badge": "0.8.8",
|
||||
"cordova-plugin-camera": "4.0.3",
|
||||
"cordova-plugin-customurlscheme": "4.3.0",
|
||||
"cordova-plugin-device": "2.0.2",
|
||||
"cordova-plugin-file": "6.0.1",
|
||||
"cordova-plugin-camera": "4.1.0",
|
||||
"cordova-plugin-customurlscheme": "4.4.0",
|
||||
"cordova-plugin-device": "2.0.3",
|
||||
"cordova-plugin-file": "6.0.2",
|
||||
"cordova-plugin-file-opener2": "2.0.19",
|
||||
"cordova-plugin-file-transfer": "1.7.1",
|
||||
"cordova-plugin-globalization": "1.11.0",
|
||||
"cordova-plugin-inappbrowser": "3.0.0",
|
||||
"cordova-plugin-inappbrowser": "3.1.0",
|
||||
"cordova-plugin-ionic-keyboard": "2.1.3",
|
||||
"cordova-plugin-local-notification": "git+https://github.com/moodlemobile/cordova-plugin-local-notification.git#moodle",
|
||||
"cordova-plugin-media-capture": "3.0.2",
|
||||
"cordova-plugin-network-information": "2.0.1",
|
||||
"cordova-plugin-screen-orientation": "3.0.1",
|
||||
"cordova-plugin-splashscreen": "5.0.2",
|
||||
"cordova-plugin-statusbar": "2.4.2",
|
||||
"cordova-plugin-whitelist": "1.3.3",
|
||||
"cordova-plugin-media-capture": "3.0.3",
|
||||
"cordova-plugin-network-information": "2.0.2",
|
||||
"cordova-plugin-screen-orientation": "3.0.2",
|
||||
"cordova-plugin-splashscreen": "5.0.3",
|
||||
"cordova-plugin-statusbar": "2.4.3",
|
||||
"cordova-plugin-whitelist": "1.3.4",
|
||||
"cordova-plugin-zip": "3.1.0",
|
||||
"cordova-sqlite-storage": "2.6.0",
|
||||
"cordova-support-google-services": "1.2.1",
|
||||
|
@ -124,7 +125,7 @@
|
|||
"@ionic/app-scripts": "3.2.2",
|
||||
"electron-builder-lib": "20.23.1",
|
||||
"electron-rebuild": "1.8.1",
|
||||
"gulp": "4.0.0",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-clip-empty-files": "0.1.2",
|
||||
"gulp-concat": "2.6.1",
|
||||
"gulp-flatten": "0.4.0",
|
||||
|
@ -210,11 +211,12 @@
|
|||
}
|
||||
],
|
||||
"compression": "maximum",
|
||||
"electronVersion": "4.0.1",
|
||||
"electronVersion": "4.2.5",
|
||||
"mac": {
|
||||
"category": "public.app-category.education",
|
||||
"icon": "resources/desktop/icon.icns",
|
||||
"target": "mas",
|
||||
"bundleVersion": "3.7.0",
|
||||
"extendInfo": {
|
||||
"ElectronTeamID": "2NU57U5PAW"
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
# List first level of installed libraries so we can check the installed versions.
|
||||
npm list --depth=0
|
||||
|
||||
# Compile AOT.
|
||||
if [ $TRAVIS_BRANCH == 'integration' ] || [ $TRAVIS_BRANCH == 'master' ] || [ $TRAVIS_BRANCH == 'desktop' ] || [ -z $TRAVIS_BRANCH ] ; then
|
||||
cd scripts
|
||||
|
@ -40,17 +43,34 @@ if [ ! -z $GIT_ORG ] && [ ! -z $GIT_TOKEN ] ; then
|
|||
gitfolder=${PWD##*/}
|
||||
git clone --depth 1 --no-single-branch https://github.com/$GIT_ORG/moodlemobile-phonegapbuild.git ../pgb
|
||||
pushd ../pgb
|
||||
|
||||
mkdir /tmp/travistemp
|
||||
cp .travis.yml /tmp/travistemp
|
||||
mkdir /tmp/travistemp/scripts
|
||||
cp scripts/* /tmp/travistemp/scripts
|
||||
|
||||
git checkout $TRAVIS_BRANCH
|
||||
rm -Rf assets build index.html templates
|
||||
cp -Rf ../$gitfolder/www/* ./
|
||||
rm -Rf assets/countries assets/mimetypes
|
||||
|
||||
rm -Rf assets build index.html templates www destkop
|
||||
|
||||
if [ $TRAVIS_BRANCH == 'desktop' ] ; then
|
||||
cp -Rf ../$gitfolder/desktop ./
|
||||
cp -Rf ../$gitfolder/package.json ./
|
||||
cp -Rf ../$gitfolder/www ./
|
||||
rm -Rf www/assets/countries www/assets/mimetypes
|
||||
else
|
||||
cp -Rf ../$gitfolder/www/* ./
|
||||
rm -Rf assets/countries assets/mimetypes
|
||||
fi
|
||||
|
||||
cp /tmp/travistemp/.travis.yml .travis.yml
|
||||
mkdir scripts
|
||||
cp /tmp/travistemp/scripts/* scripts
|
||||
|
||||
|
||||
git add .
|
||||
git commit -m "Travis build: $TRAVIS_BUILD_NUMBER"
|
||||
git push https://$GIT_TOKEN@github.com/$GIT_ORG/moodlemobile-phonegapbuild.git
|
||||
popd
|
||||
fi
|
||||
|
||||
if [ ! -z $GIT_ORG_PRIVATE ] && [ ! -z $GIT_TOKEN ] && [ $TRAVIS_BRANCH == 'desktop' ] && [ $TRAVIS_OS_NAME == 'linux' ]; then
|
||||
./scripts/linux.sh
|
||||
fi
|
||||
|
||||
|
|
|
@ -0,0 +1,426 @@
|
|||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Helper functions converting moodle strings to json.
|
||||
*/
|
||||
|
||||
function detect_languages($languages, $keys) {
|
||||
echo "\n\n\n";
|
||||
|
||||
$all_languages = glob(LANGPACKSFOLDER.'/*' , GLOB_ONLYDIR);
|
||||
function get_lang_from_dir($dir) {
|
||||
return str_replace('_', '-', explode('/', $dir)[3]);
|
||||
}
|
||||
function get_lang_not_wp($langname) {
|
||||
return (substr($langname, -3) !== '-wp');
|
||||
}
|
||||
$all_languages = array_map('get_lang_from_dir', $all_languages);
|
||||
$all_languages = array_filter($all_languages, 'get_lang_not_wp');
|
||||
|
||||
$detect_lang = array_diff($all_languages, $languages);
|
||||
$new_langs = array();
|
||||
foreach ($detect_lang as $lang) {
|
||||
reset_translations_strings();
|
||||
$new = detect_lang($lang, $keys);
|
||||
if ($new) {
|
||||
$new_langs[$lang] = $lang;
|
||||
}
|
||||
}
|
||||
|
||||
return $new_langs;
|
||||
}
|
||||
|
||||
function build_languages($languages, $keys, $added_langs = []) {
|
||||
// Process the languages.
|
||||
foreach ($languages as $lang) {
|
||||
reset_translations_strings();
|
||||
$ok = build_lang($lang, $keys);
|
||||
if ($ok) {
|
||||
$added_langs[$lang] = $lang;
|
||||
}
|
||||
}
|
||||
|
||||
return $added_langs;
|
||||
}
|
||||
|
||||
function get_langindex_keys() {
|
||||
// Process the index file, just once.
|
||||
$keys = file_get_contents('langindex.json');
|
||||
$keys = (array) json_decode($keys);
|
||||
|
||||
foreach ($keys as $key => $value) {
|
||||
$map = new StdClass();
|
||||
if ($value == 'local_moodlemobileapp') {
|
||||
$map->file = $value;
|
||||
$map->string = $key;
|
||||
} else {
|
||||
$exp = explode('/', $value, 2);
|
||||
$map->file = $exp[0];
|
||||
if (count($exp) == 2) {
|
||||
$map->string = $exp[1];
|
||||
} else {
|
||||
$exp = explode('.', $key, 3);
|
||||
|
||||
if (count($exp) == 3) {
|
||||
$map->string = $exp[2];
|
||||
} else {
|
||||
$map->string = $exp[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$keys[$key] = $map;
|
||||
}
|
||||
|
||||
$total = count($keys);
|
||||
echo "Total strings to translate $total\n";
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
function add_langs_to_config($langs, $config) {
|
||||
$changed = false;
|
||||
$config_langs = get_object_vars($config['languages']);
|
||||
foreach ($langs as $lang) {
|
||||
if (!isset($config_langs[$lang])) {
|
||||
$langfoldername = str_replace('-', '_', $lang);
|
||||
|
||||
$string = [];
|
||||
include(LANGPACKSFOLDER.'/'.$langfoldername.'/langconfig.php');
|
||||
$config['languages']->$lang = $string['thislanguage'];
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($changed) {
|
||||
// Sort languages by key.
|
||||
$config['languages'] = json_decode( json_encode( $config['languages'] ), true );
|
||||
ksort($config['languages']);
|
||||
$config['languages'] = json_decode( json_encode( $config['languages'] ), false );
|
||||
file_put_contents(CONFIG, json_encode($config, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
|
||||
}
|
||||
}
|
||||
|
||||
function get_langfolder($lang) {
|
||||
$folder = LANGPACKSFOLDER.'/'.str_replace('-', '_', $lang);
|
||||
if (!is_dir($folder) || !is_file($folder.'/langconfig.php')) {
|
||||
echo "Cannot translate $folder, folder not found";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return $folder;
|
||||
}
|
||||
|
||||
function get_translation_strings($langfoldername, $file, $override_folder = false) {
|
||||
global $strings;
|
||||
|
||||
if (isset($strings[$file])) {
|
||||
return $strings[$file];
|
||||
}
|
||||
|
||||
$string = import_translation_strings($langfoldername, $file);
|
||||
$string_override = $override_folder ? import_translation_strings($override_folder, $file) : false;
|
||||
|
||||
if ($string) {
|
||||
$strings[$file] = $string;
|
||||
if ($string_override) {
|
||||
$strings[$file] = array_merge($strings[$file], $string_override);
|
||||
}
|
||||
} else if ($string_override) {
|
||||
$strings[$file] = $string_override;
|
||||
} else {
|
||||
$strings[$file] = false;
|
||||
}
|
||||
|
||||
return $strings[$file];
|
||||
}
|
||||
|
||||
function import_translation_strings($langfoldername, $file) {
|
||||
$path = $langfoldername.'/'.$file.'.php';
|
||||
// Apply translations.
|
||||
if (!file_exists($path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$string = [];
|
||||
include($path);
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
function reset_translations_strings() {
|
||||
global $strings;
|
||||
$strings = [];
|
||||
}
|
||||
|
||||
function build_lang($lang, $keys) {
|
||||
$langfoldername = get_langfolder($lang);
|
||||
if (!$langfoldername) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (OVERRIDE_LANG_SUFIX) {
|
||||
$override_langfolder = get_langfolder($lang.OVERRIDE_LANG_SUFIX);
|
||||
} else {
|
||||
$override_langfolder = false;
|
||||
}
|
||||
|
||||
$total = count ($keys);
|
||||
$local = 0;
|
||||
|
||||
$string = get_translation_strings($langfoldername, 'langconfig');
|
||||
$parent = isset($string['parentlanguage']) ? $string['parentlanguage'] : "";
|
||||
|
||||
echo "Processing $lang";
|
||||
if ($parent != "" && $parent != $lang) {
|
||||
echo " ($parent)";
|
||||
}
|
||||
|
||||
$langFile = false;
|
||||
// Not yet translated. Do not override.
|
||||
if (file_exists(ASSETSPATH.$lang.'.json')) {
|
||||
// Load lang files just once.
|
||||
$langFile = file_get_contents(ASSETSPATH.$lang.'.json');
|
||||
$langFile = (array) json_decode($langFile);
|
||||
}
|
||||
|
||||
$translations = [];
|
||||
// Add the translation to the array.
|
||||
foreach ($keys as $key => $value) {
|
||||
$string = get_translation_strings($langfoldername, $value->file, $override_langfolder);
|
||||
// Apply translations.
|
||||
if (!$string) {
|
||||
if (TOTRANSLATE) {
|
||||
echo "\n\t\To translate $value->string on $value->file";
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($string[$value->string]) || ($lang == 'en' && $value->file == 'local_moodlemobileapp')) {
|
||||
// Not yet translated. Do not override.
|
||||
if ($langFile && is_array($langFile) && isset($langFile[$key])) {
|
||||
$translations[$key] = $langFile[$key];
|
||||
$local++;
|
||||
}
|
||||
if (TOTRANSLATE) {
|
||||
echo "\n\t\tTo translate $value->string on $value->file";
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
$text = $string[$value->string];
|
||||
}
|
||||
|
||||
if ($value->file != 'local_moodlemobileapp') {
|
||||
$text = str_replace('$a->', '$a.', $text);
|
||||
$text = str_replace('{$a', '{{$a', $text);
|
||||
$text = str_replace('}', '}}', $text);
|
||||
// Prevent double.
|
||||
$text = str_replace(array('{{{', '}}}'), array('{{', '}}'), $text);
|
||||
} else {
|
||||
$local++;
|
||||
}
|
||||
|
||||
$translations[$key] = html_entity_decode($text);
|
||||
}
|
||||
|
||||
// Sort and save.
|
||||
ksort($translations);
|
||||
file_put_contents(ASSETSPATH.$lang.'.json', str_replace('\/', '/', json_encode($translations, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)));
|
||||
|
||||
$success = count($translations);
|
||||
$percentage = floor($success/$total * 100);
|
||||
echo "\t\t$success of $total -> $percentage% ($local local)\n";
|
||||
|
||||
if ($lang == 'en') {
|
||||
generate_local_moodlemobileapp($keys, $translations);
|
||||
override_component_lang_files($keys, $translations);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function detect_lang($lang, $keys) {
|
||||
$langfoldername = get_langfolder($lang);
|
||||
if (!$langfoldername) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$total = count ($keys);
|
||||
$success = 0;
|
||||
$local = 0;
|
||||
|
||||
$string = get_translation_strings($langfoldername, 'langconfig');
|
||||
$parent = isset($string['parentlanguage']) ? $string['parentlanguage'] : "";
|
||||
if (!isset($string['thislanguage'])) {
|
||||
echo "Cannot translate $lang, translated name not found";
|
||||
return false;
|
||||
}
|
||||
|
||||
echo "Checking $lang";
|
||||
if ($parent != "" && $parent != $lang) {
|
||||
echo " ($parent)";
|
||||
}
|
||||
$langname = $string['thislanguage'];
|
||||
echo " ".$langname." -D";
|
||||
|
||||
// Add the translation to the array.
|
||||
foreach ($keys as $key => $value) {
|
||||
$string = get_translation_strings($langfoldername, $value->file);
|
||||
// Apply translations.
|
||||
if (!$string) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($string[$value->string])) {
|
||||
continue;
|
||||
} else {
|
||||
$text = $string[$value->string];
|
||||
}
|
||||
|
||||
if ($value->file == 'local_moodlemobileapp') {
|
||||
$local++;
|
||||
}
|
||||
|
||||
$success++;
|
||||
}
|
||||
|
||||
$percentage = floor($success/$total * 100);
|
||||
echo "\t\t$success of $total -> $percentage% ($local local)";
|
||||
if (($percentage > 75 && $local > 50) || ($percentage > 50 && $local > 75)) {
|
||||
echo " \t DETECTED\n";
|
||||
return true;
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function save_key($key, $value, $path) {
|
||||
$filePath = $path . '/en.json';
|
||||
|
||||
$file = file_get_contents($filePath);
|
||||
$file = (array) json_decode($file);
|
||||
$value = html_entity_decode($value);
|
||||
if (!isset($file[$key]) || $file[$key] != $value) {
|
||||
$file[$key] = $value;
|
||||
ksort($file);
|
||||
file_put_contents($filePath, str_replace('\/', '/', json_encode($file, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)));
|
||||
}
|
||||
}
|
||||
|
||||
function override_component_lang_files($keys, $translations) {
|
||||
echo "Override component lang files.\n";
|
||||
foreach ($translations as $key => $value) {
|
||||
$path = '../src/';
|
||||
$exp = explode('.', $key, 3);
|
||||
|
||||
$type = $exp[0];
|
||||
if (count($exp) == 3) {
|
||||
$component = $exp[1];
|
||||
$plainid = $exp[2];
|
||||
} else {
|
||||
$component = 'moodle';
|
||||
$plainid = $exp[1];
|
||||
}
|
||||
switch($type) {
|
||||
case 'core':
|
||||
case 'addon':
|
||||
switch($component) {
|
||||
case 'moodle':
|
||||
$path .= 'lang';
|
||||
break;
|
||||
default:
|
||||
$path .= $type.'/'.str_replace('_', '/', $component).'/lang';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'assets':
|
||||
$path .= $type.'/'.$component;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (is_file($path.'/en.json')) {
|
||||
save_key($plainid, $value, $path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates local moodle mobile app file to update languages in AMOS.
|
||||
*
|
||||
* @param [array] $keys Translation keys.
|
||||
* @param [array] $translations English translations.
|
||||
*/
|
||||
function generate_local_moodlemobileapp($keys, $translations) {
|
||||
echo "Generate local_moodlemobileapp.\n";
|
||||
$string = '<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Version details.
|
||||
*
|
||||
* @package local
|
||||
* @subpackage moodlemobileapp
|
||||
* @copyright 2014 Juan Leyva <juanleyvadelgado@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
$string[\'appstoredescription\'] = \'NOTE: This official Moodle Mobile app will ONLY work with Moodle sites that have been set up to allow it. Please talk to your Moodle administrator if you have any problems connecting.
|
||||
|
||||
If your Moodle site has been configured correctly, you can use this app to:
|
||||
|
||||
- browse the content of your courses, even when offline
|
||||
- receive instant notifications of messages and other events
|
||||
- quickly find and contact other people in your courses
|
||||
- upload images, audio, videos and other files from your mobile device
|
||||
- view your course grades
|
||||
- and more!
|
||||
|
||||
Please see http://docs.moodle.org/en/Mobile_app for all the latest information.
|
||||
|
||||
We’d really appreciate any good reviews about the functionality so far, and your suggestions on what else you want this app to do!
|
||||
|
||||
The app requires the following permissions:
|
||||
Record audio - For recording audio to upload to Moodle
|
||||
Read and modify the contents of your SD card - Contents are downloaded to the SD Card so you can see them offline
|
||||
Network access - To be able to connect with your Moodle site and check if you are connected or not to switch to offline mode
|
||||
Run at startup - So you receive local notifications even when the app is running in the background
|
||||
Prevent phone from sleeping - So you can receive push notifications anytime\';'."\n";
|
||||
foreach ($keys as $key => $value) {
|
||||
if (isset($translations[$key]) && $value->file == 'local_moodlemobileapp') {
|
||||
$string .= '$string[\''.$key.'\'] = \''.str_replace("'", "\'", $translations[$key]).'\';'."\n";
|
||||
}
|
||||
}
|
||||
$string .= '$string[\'pluginname\'] = \'Moodle Mobile language strings\';'."\n";
|
||||
|
||||
file_put_contents('../../moodle-local_moodlemobileapp/lang/en/local_moodlemobileapp.php', $string."\n");
|
||||
}
|
|
@ -27,6 +27,17 @@
|
|||
"addon.badges.version": "badges",
|
||||
"addon.badges.warnexpired": "badges",
|
||||
"addon.block_activitymodules.pluginname": "block_activity_modules",
|
||||
"addon.block_badges.pluginname": "block_badges",
|
||||
"addon.block_blogmenu.pluginname": "block_blog_menu",
|
||||
"addon.block_blogrecent.nocourses": "block_blog_recent",
|
||||
"addon.block_blogrecent.pluginname": "block_blog_recent",
|
||||
"addon.block_blogtags.pluginname": "block_blog_tags",
|
||||
"addon.block_calendarmonth.pluginname": "block_calendar_month",
|
||||
"addon.block_calendarupcoming.pluginname": "block_calendar_upcoming",
|
||||
"addon.block_comments.pluginname": "block_comments",
|
||||
"addon.block_completionstatus.pluginname": "block_completionstatus",
|
||||
"addon.block_glossaryrandom.pluginname": "block_glossary_random",
|
||||
"addon.block_learningplans.pluginname": "block_lp",
|
||||
"addon.block_myoverview.all": "block_myoverview",
|
||||
"addon.block_myoverview.favourites": "block_myoverview",
|
||||
"addon.block_myoverview.future": "block_myoverview",
|
||||
|
@ -38,13 +49,20 @@
|
|||
"addon.block_myoverview.past": "block_myoverview",
|
||||
"addon.block_myoverview.pluginname": "block_myoverview",
|
||||
"addon.block_myoverview.title": "block_myoverview",
|
||||
"addon.block_newsitems.pluginname": "block_news_items",
|
||||
"addon.block_onlineusers.pluginname": "block_online_users",
|
||||
"addon.block_privatefiles.pluginname": "block_private_files",
|
||||
"addon.block_recentactivity.pluginname": "block_recent_activity",
|
||||
"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_rssclient.pluginname": "block_rss_client",
|
||||
"addon.block_selfcompletion.pluginname": "block_selfcompletion",
|
||||
"addon.block_sitemainmenu.pluginname": "block_site_main_menu",
|
||||
"addon.block_starredcourses.nocourses": "block_starredcourses",
|
||||
"addon.block_starredcourses.pluginname": "block_starredcourses",
|
||||
"addon.block_tags.pluginname": "block_tags",
|
||||
"addon.block_timeline.duedate": "block_timeline",
|
||||
"addon.block_timeline.next30days": "block_timeline",
|
||||
"addon.block_timeline.next3months": "block_timeline",
|
||||
|
@ -66,18 +84,61 @@
|
|||
"addon.blog.publishtoworld": "blog",
|
||||
"addon.blog.showonlyyourentries": "local_moodlemobileapp",
|
||||
"addon.blog.siteblogheading": "blog",
|
||||
"addon.calendar.allday": "calendar",
|
||||
"addon.calendar.calendar": "calendar",
|
||||
"addon.calendar.calendarevent": "local_moodlemobileapp",
|
||||
"addon.calendar.calendarevents": "local_moodlemobileapp",
|
||||
"addon.calendar.calendarreminders": "local_moodlemobileapp",
|
||||
"addon.calendar.confirmeventdelete": "calendar",
|
||||
"addon.calendar.confirmeventseriesdelete": "calendar",
|
||||
"addon.calendar.currentmonth": "local_moodlemobileapp",
|
||||
"addon.calendar.daynext": "calendar",
|
||||
"addon.calendar.dayprev": "calendar",
|
||||
"addon.calendar.defaultnotificationtime": "local_moodlemobileapp",
|
||||
"addon.calendar.deleteallevents": "calendar",
|
||||
"addon.calendar.deleteevent": "calendar",
|
||||
"addon.calendar.deleteoneevent": "calendar",
|
||||
"addon.calendar.durationminutes": "calendar",
|
||||
"addon.calendar.durationnone": "calendar",
|
||||
"addon.calendar.durationuntil": "calendar",
|
||||
"addon.calendar.editevent": "calendar",
|
||||
"addon.calendar.errorloadevent": "local_moodlemobileapp",
|
||||
"addon.calendar.errorloadevents": "local_moodlemobileapp",
|
||||
"addon.calendar.eventcalendareventdeleted": "calendar",
|
||||
"addon.calendar.eventduration": "calendar",
|
||||
"addon.calendar.eventendtime": "calendar",
|
||||
"addon.calendar.eventkind": "calendar",
|
||||
"addon.calendar.eventname": "calendar",
|
||||
"addon.calendar.eventstarttime": "calendar",
|
||||
"addon.calendar.eventtype": "calendar",
|
||||
"addon.calendar.fri": "calendar",
|
||||
"addon.calendar.friday": "calendar",
|
||||
"addon.calendar.gotoactivity": "calendar",
|
||||
"addon.calendar.invalidtimedurationminutes": "calendar",
|
||||
"addon.calendar.invalidtimedurationuntil": "calendar",
|
||||
"addon.calendar.mon": "calendar",
|
||||
"addon.calendar.monday": "calendar",
|
||||
"addon.calendar.monthlyview": "calendar",
|
||||
"addon.calendar.newevent": "calendar",
|
||||
"addon.calendar.noevents": "local_moodlemobileapp",
|
||||
"addon.calendar.nopermissiontoupdatecalendar": "error",
|
||||
"addon.calendar.reminders": "local_moodlemobileapp",
|
||||
"addon.calendar.repeatedevents": "calendar",
|
||||
"addon.calendar.repeateditall": "calendar",
|
||||
"addon.calendar.repeateditthis": "calendar",
|
||||
"addon.calendar.repeatevent": "calendar",
|
||||
"addon.calendar.repeatweeksl": "calendar",
|
||||
"addon.calendar.sat": "calendar",
|
||||
"addon.calendar.saturday": "calendar",
|
||||
"addon.calendar.setnewreminder": "local_moodlemobileapp",
|
||||
"addon.calendar.sun": "calendar",
|
||||
"addon.calendar.sunday": "calendar",
|
||||
"addon.calendar.thu": "calendar",
|
||||
"addon.calendar.thursday": "calendar",
|
||||
"addon.calendar.today": "calendar",
|
||||
"addon.calendar.tomorrow": "calendar",
|
||||
"addon.calendar.tue": "calendar",
|
||||
"addon.calendar.tuesday": "calendar",
|
||||
"addon.calendar.typecategory": "calendar",
|
||||
"addon.calendar.typeclose": "calendar",
|
||||
"addon.calendar.typecourse": "calendar",
|
||||
|
@ -87,6 +148,11 @@
|
|||
"addon.calendar.typeopen": "calendar",
|
||||
"addon.calendar.typesite": "calendar",
|
||||
"addon.calendar.typeuser": "calendar",
|
||||
"addon.calendar.upcomingevents": "calendar",
|
||||
"addon.calendar.wed": "calendar",
|
||||
"addon.calendar.wednesday": "calendar",
|
||||
"addon.calendar.when": "calendar",
|
||||
"addon.calendar.yesterday": "calendar",
|
||||
"addon.competency.activities": "tool_lp",
|
||||
"addon.competency.competencies": "competency",
|
||||
"addon.competency.competenciesmostoftennotproficientincourse": "tool_lp",
|
||||
|
@ -355,6 +421,7 @@
|
|||
"addon.mod_assign_submission_onlinetext.wordlimitexceeded": "assignsubmission_onlinetext",
|
||||
"addon.mod_book.errorchapter": "book",
|
||||
"addon.mod_book.modulenameplural": "book",
|
||||
"addon.mod_book.tagarea_book_chapters": "book",
|
||||
"addon.mod_book.toc": "book",
|
||||
"addon.mod_chat.beep": "chat",
|
||||
"addon.mod_chat.chatreport": "chat",
|
||||
|
@ -414,6 +481,7 @@
|
|||
"addon.mod_data.confirmdeleterecord": "data",
|
||||
"addon.mod_data.descending": "data",
|
||||
"addon.mod_data.disapprove": "data",
|
||||
"addon.mod_data.edittagsnotsupported": "local_moodlemobileapp",
|
||||
"addon.mod_data.emptyaddform": "data",
|
||||
"addon.mod_data.entrieslefttoadd": "data",
|
||||
"addon.mod_data.entrieslefttoaddtoview": "data",
|
||||
|
@ -438,8 +506,10 @@
|
|||
"addon.mod_data.recorddisapproved": "data",
|
||||
"addon.mod_data.resetsettings": "data",
|
||||
"addon.mod_data.search": "data",
|
||||
"addon.mod_data.searchbytagsnotsupported": "local_moodlemobileapp",
|
||||
"addon.mod_data.selectedrequired": "data",
|
||||
"addon.mod_data.single": "data",
|
||||
"addon.mod_data.tagarea_data_records": "data",
|
||||
"addon.mod_data.timeadded": "data",
|
||||
"addon.mod_data.timemodified": "data",
|
||||
"addon.mod_data.usedate": "data",
|
||||
|
@ -532,6 +602,7 @@
|
|||
"addon.mod_forum.reply": "forum",
|
||||
"addon.mod_forum.replyplaceholder": "forum",
|
||||
"addon.mod_forum.subject": "forum",
|
||||
"addon.mod_forum.tagarea_forum_posts": "forum",
|
||||
"addon.mod_forum.thisforumhasduedate": "forum",
|
||||
"addon.mod_forum.thisforumisdue": "forum",
|
||||
"addon.mod_forum.unlockdiscussion": "forum",
|
||||
|
@ -566,9 +637,11 @@
|
|||
"addon.mod_glossary.modulenameplural": "glossary",
|
||||
"addon.mod_glossary.noentriesfound": "local_moodlemobileapp",
|
||||
"addon.mod_glossary.searchquery": "local_moodlemobileapp",
|
||||
"addon.mod_glossary.tagarea_glossary_entries": "glossary",
|
||||
"addon.mod_imscp.deploymenterror": "imscp",
|
||||
"addon.mod_imscp.modulenameplural": "imscp",
|
||||
"addon.mod_imscp.showmoduledescription": "local_moodlemobileapp",
|
||||
"addon.mod_imscp.toc": "imscp",
|
||||
"addon.mod_lesson.answer": "lesson",
|
||||
"addon.mod_lesson.attempt": "lesson",
|
||||
"addon.mod_lesson.attemptheader": "lesson",
|
||||
|
@ -665,6 +738,7 @@
|
|||
"addon.mod_quiz.attemptnumber": "quiz",
|
||||
"addon.mod_quiz.attemptquiznow": "quiz",
|
||||
"addon.mod_quiz.attemptstate": "quiz",
|
||||
"addon.mod_quiz.canattemptbutnotsubmit": "local_moodlemobileapp",
|
||||
"addon.mod_quiz.cannotsubmitquizdueto": "local_moodlemobileapp",
|
||||
"addon.mod_quiz.clearchoice": "qtype_multichoice",
|
||||
"addon.mod_quiz.comment": "quiz",
|
||||
|
@ -737,6 +811,7 @@
|
|||
"addon.mod_quiz.warningattemptfinished": "local_moodlemobileapp",
|
||||
"addon.mod_quiz.warningdatadiscarded": "local_moodlemobileapp",
|
||||
"addon.mod_quiz.warningdatadiscardedfromfinished": "local_moodlemobileapp",
|
||||
"addon.mod_quiz.warningquestionsnotsupported": "local_moodlemobileapp",
|
||||
"addon.mod_quiz.yourfinalgradeis": "quiz",
|
||||
"addon.mod_resource.errorwhileloadingthecontent": "local_moodlemobileapp",
|
||||
"addon.mod_resource.modifieddate": "resource",
|
||||
|
@ -820,6 +895,7 @@
|
|||
"addon.mod_wiki.pageexists": "wiki",
|
||||
"addon.mod_wiki.pagename": "wiki",
|
||||
"addon.mod_wiki.subwiki": "local_moodlemobileapp",
|
||||
"addon.mod_wiki.tagarea_wiki_pages": "wiki",
|
||||
"addon.mod_wiki.titleshouldnotbeempty": "local_moodlemobileapp",
|
||||
"addon.mod_wiki.viewpage": "local_moodlemobileapp",
|
||||
"addon.mod_wiki.wikipage": "local_moodlemobileapp",
|
||||
|
@ -898,7 +974,9 @@
|
|||
"addon.mod_workshop_assessment_rubric.mustchooseone": "workshopform_rubric",
|
||||
"addon.notes.addnewnote": "notes",
|
||||
"addon.notes.coursenotes": "notes",
|
||||
"addon.notes.deleteconfirm": "notes",
|
||||
"addon.notes.eventnotecreated": "notes",
|
||||
"addon.notes.eventnotedeleted": "notes",
|
||||
"addon.notes.nonotes": "notes",
|
||||
"addon.notes.note": "notes",
|
||||
"addon.notes.notes": "notes",
|
||||
|
@ -1231,6 +1309,7 @@
|
|||
"core.answered": "quiz",
|
||||
"core.areyousure": "moodle",
|
||||
"core.back": "moodle",
|
||||
"core.block.blocks": "moodle",
|
||||
"core.cancel": "moodle",
|
||||
"core.cannotconnect": "local_moodlemobileapp",
|
||||
"core.cannotdownloadfiles": "local_moodlemobileapp",
|
||||
|
@ -1246,6 +1325,16 @@
|
|||
"core.clicktoseefull": "local_moodlemobileapp",
|
||||
"core.close": "repository",
|
||||
"core.comments": "moodle",
|
||||
"core.comments.addcomment": "moodle",
|
||||
"core.comments.comments": "moodle",
|
||||
"core.comments.commentscount": "moodle",
|
||||
"core.comments.commentsnotworking": "local_moodlemobileapp",
|
||||
"core.comments.deletecommentbyon": "moodle",
|
||||
"core.comments.eventcommentcreated": "moodle",
|
||||
"core.comments.eventcommentdeleted": "moodle",
|
||||
"core.comments.nocomments": "moodle",
|
||||
"core.comments.savecomment": "moodle",
|
||||
"core.comments.warningcommentsnotsent": "local_moodlemobileapp",
|
||||
"core.commentscount": "moodle",
|
||||
"core.commentsnotworking": "local_moodlemobileapp",
|
||||
"core.completion-alt-auto-fail": "completion",
|
||||
|
@ -1260,6 +1349,8 @@
|
|||
"core.completion-alt-manual-y-override": "completion",
|
||||
"core.confirmcanceledit": "local_moodlemobileapp",
|
||||
"core.confirmdeletefile": "repository",
|
||||
"core.confirmgotabroot": "local_moodlemobileapp",
|
||||
"core.confirmgotabrootdefault": "local_moodlemobileapp",
|
||||
"core.confirmloss": "local_moodlemobileapp",
|
||||
"core.confirmopeninbrowser": "local_moodlemobileapp",
|
||||
"core.considereddigitalminor": "moodle",
|
||||
|
@ -1306,6 +1397,7 @@
|
|||
"core.course.warningmanualcompletionmodified": "local_moodlemobileapp",
|
||||
"core.course.warningofflinemanualcompletiondeleted": "local_moodlemobileapp",
|
||||
"core.coursedetails": "moodle",
|
||||
"core.coursenogroups": "local_moodlemobileapp",
|
||||
"core.courses.addtofavourites": "block_myoverview",
|
||||
"core.courses.allowguests": "enrol_guest",
|
||||
"core.courses.availablecourses": "moodle",
|
||||
|
@ -1323,6 +1415,7 @@
|
|||
"core.courses.filtermycourses": "local_moodlemobileapp",
|
||||
"core.courses.frontpage": "admin",
|
||||
"core.courses.hidecourse": "block_myoverview",
|
||||
"core.courses.ignore": "admin",
|
||||
"core.courses.mycourses": "moodle",
|
||||
"core.courses.mymoodle": "admin",
|
||||
"core.courses.nocourses": "my",
|
||||
|
@ -1333,6 +1426,7 @@
|
|||
"core.courses.password": "local_moodlemobileapp",
|
||||
"core.courses.paymentrequired": "moodle",
|
||||
"core.courses.paypalaccepted": "enrol_paypal",
|
||||
"core.courses.reload": "moodle",
|
||||
"core.courses.removefromfavourites": "block_myoverview",
|
||||
"core.courses.search": "moodle",
|
||||
"core.courses.searchcourses": "moodle",
|
||||
|
@ -1383,6 +1477,7 @@
|
|||
"core.erroropenfilenoextension": "local_moodlemobileapp",
|
||||
"core.erroropenpopup": "local_moodlemobileapp",
|
||||
"core.errorrenamefile": "local_moodlemobileapp",
|
||||
"core.errorsomedatanotdownloaded": "local_moodlemobileapp",
|
||||
"core.errorsync": "local_moodlemobileapp",
|
||||
"core.errorsyncblocked": "local_moodlemobileapp",
|
||||
"core.explanationdigitalminor": "moodle",
|
||||
|
@ -1435,6 +1530,7 @@
|
|||
"core.grades.range": "grades",
|
||||
"core.grades.rank": "grades",
|
||||
"core.grades.weight": "grades",
|
||||
"core.group": "moodle",
|
||||
"core.groupsseparate": "moodle",
|
||||
"core.groupsvisible": "moodle",
|
||||
"core.hasdatatosync": "local_moodlemobileapp",
|
||||
|
@ -1446,6 +1542,7 @@
|
|||
"core.image": "local_moodlemobileapp",
|
||||
"core.imageviewer": "local_moodlemobileapp",
|
||||
"core.info": "moodle",
|
||||
"core.invalidformdata": "error",
|
||||
"core.ios": "local_moodlemobileapp",
|
||||
"core.labelsep": "langconfig",
|
||||
"core.lastaccess": "moodle",
|
||||
|
@ -1465,6 +1562,8 @@
|
|||
"core.login.confirmdeletesite": "local_moodlemobileapp",
|
||||
"core.login.connect": "local_moodlemobileapp",
|
||||
"core.login.connecttomoodle": "local_moodlemobileapp",
|
||||
"core.login.connecttomoodleapp": "local_moodlemobileapp",
|
||||
"core.login.connecttoworkplaceapp": "local_moodlemobileapp",
|
||||
"core.login.contactyouradministrator": "local_moodlemobileapp",
|
||||
"core.login.contactyouradministratorissue": "local_moodlemobileapp",
|
||||
"core.login.createaccount": "moodle",
|
||||
|
@ -1602,12 +1701,14 @@
|
|||
"core.nopermissionerror": "local_moodlemobileapp",
|
||||
"core.nopermissions": "error",
|
||||
"core.noresults": "moodle",
|
||||
"core.noselection": "form",
|
||||
"core.notapplicable": "local_moodlemobileapp",
|
||||
"core.notenrolledprofile": "moodle",
|
||||
"core.notice": "moodle",
|
||||
"core.notingroup": "moodle",
|
||||
"core.notsent": "local_moodlemobileapp",
|
||||
"core.now": "moodle",
|
||||
"core.nummore": "local_moodlemobileapp",
|
||||
"core.numwords": "moodle",
|
||||
"core.offline": "message",
|
||||
"core.ok": "moodle",
|
||||
|
@ -1670,6 +1771,9 @@
|
|||
"core.sec": "moodle",
|
||||
"core.secs": "moodle",
|
||||
"core.seemoredetail": "survey",
|
||||
"core.selectacategory": "moodle",
|
||||
"core.selectacourse": "moodle",
|
||||
"core.selectagroup": "moodle",
|
||||
"core.send": "message",
|
||||
"core.sending": "chat",
|
||||
"core.serverconnection": "error",
|
||||
|
@ -1695,6 +1799,8 @@
|
|||
"core.settings.disabled": "lesson",
|
||||
"core.settings.displayformat": "local_moodlemobileapp",
|
||||
"core.settings.enabledownloadsection": "local_moodlemobileapp",
|
||||
"core.settings.enablefirebaseanalytics": "local_moodlemobileapp",
|
||||
"core.settings.enablefirebaseanalyticsdescription": "local_moodlemobileapp",
|
||||
"core.settings.enablerichtexteditor": "local_moodlemobileapp",
|
||||
"core.settings.enablerichtexteditordescription": "local_moodlemobileapp",
|
||||
"core.settings.enablesyncwifi": "local_moodlemobileapp",
|
||||
|
@ -1703,6 +1809,8 @@
|
|||
"core.settings.errorsyncsite": "local_moodlemobileapp",
|
||||
"core.settings.estimatedfreespace": "local_moodlemobileapp",
|
||||
"core.settings.filesystemroot": "local_moodlemobileapp",
|
||||
"core.settings.fontsize": "local_moodlemobileapp",
|
||||
"core.settings.fontsizecharacter": "block_accessibility/char",
|
||||
"core.settings.general": "moodle",
|
||||
"core.settings.language": "moodle",
|
||||
"core.settings.license": "moodle",
|
||||
|
@ -1738,6 +1846,7 @@
|
|||
"core.sharedfiles.sharedfiles": "local_moodlemobileapp",
|
||||
"core.sharedfiles.successstorefile": "local_moodlemobileapp",
|
||||
"core.show": "moodle",
|
||||
"core.showless": "form",
|
||||
"core.showmore": "form",
|
||||
"core.site": "moodle",
|
||||
"core.sitehome.sitehome": "moodle",
|
||||
|
@ -1770,6 +1879,20 @@
|
|||
"core.submit": "moodle",
|
||||
"core.success": "moodle",
|
||||
"core.tablet": "local_moodlemobileapp",
|
||||
"core.tag.defautltagcoll": "moodle",
|
||||
"core.tag.errorareanotsupported": "local_moodlemobileapp",
|
||||
"core.tag.inalltagcoll": "moodle",
|
||||
"core.tag.itemstaggedwith": "moodle",
|
||||
"core.tag.notagsfound": "moodle",
|
||||
"core.tag.searchtags": "moodle",
|
||||
"core.tag.showingfirsttags": "moodle",
|
||||
"core.tag.tag": "moodle",
|
||||
"core.tag.tagarea_course": "moodle",
|
||||
"core.tag.tagarea_course_modules": "moodle",
|
||||
"core.tag.tagarea_post": "moodle",
|
||||
"core.tag.tagarea_user": "moodle",
|
||||
"core.tag.tags": "moodle",
|
||||
"core.tag.warningareasnotsupported": "local_moodlemobileapp",
|
||||
"core.teachers": "moodle",
|
||||
"core.thereisdatatosync": "local_moodlemobileapp",
|
||||
"core.thisdirection": "langconfig",
|
||||
|
@ -1786,6 +1909,7 @@
|
|||
"core.unlimited": "moodle",
|
||||
"core.unzipping": "local_moodlemobileapp",
|
||||
"core.upgraderunning": "error",
|
||||
"core.user": "moodle",
|
||||
"core.user.address": "moodle",
|
||||
"core.user.city": "moodle",
|
||||
"core.user.contact": "local_moodlemobileapp",
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Script for generating the Desktop builds
|
||||
#
|
||||
|
||||
sudo apt-get install -y libnss3-dev
|
||||
|
||||
npm install -g electron-builder electron
|
||||
|
||||
electron-builder install-app-deps
|
||||
|
||||
jq -s '.[0] + {"name": "moodledesktop"}' package.json > package_new.json
|
||||
mv package_new.json package.json
|
||||
|
||||
rm -Rf desktop/dist
|
||||
|
||||
npm run desktop.dist -- -l --x64 --ia32
|
||||
|
||||
if [ ! -z $GIT_ORG_PRIVATE ] && [ ! -z $GIT_TOKEN ] ; then
|
||||
git clone -q https://$GIT_TOKEN@github.com/moodlemobile/bma-apps-builds.git ../apps
|
||||
|
||||
mv desktop/dist/*.AppImage ../apps
|
||||
|
||||
cd ../apps
|
||||
|
||||
chmod +x *.AppImage
|
||||
mv *i386.AppImage linux-ia32.AppImage
|
||||
mv Moodle*.AppImage linux-x64.AppImage
|
||||
ls
|
||||
|
||||
tar -czvf MoodleDesktop32.tar.gz linux-ia32.AppImage
|
||||
tar -czvf MoodleDesktop64.tar.gz linux-x64.AppImage
|
||||
rm *.AppImage
|
||||
|
||||
git add .
|
||||
git commit -m "Linux desktop versions from Travis build $TRAVIS_BUILD_NUMBER"
|
||||
git push
|
||||
fi
|
|
@ -26,6 +26,10 @@ define('MOODLE_INTERNAL', 1);
|
|||
define('LANGPACKSFOLDER', '../../moodle-langpacks');
|
||||
define('ASSETSPATH', '../src/assets/lang/');
|
||||
define('CONFIG', '../src/config.json');
|
||||
define('OVERRIDE_LANG_SUFIX', false);
|
||||
|
||||
global $strings;
|
||||
require_once('lang_functions.php');
|
||||
|
||||
$config = file_get_contents(CONFIG);
|
||||
$config = (array) json_decode($config);
|
||||
|
@ -42,355 +46,17 @@ if (isset($argv[1]) && !empty($argv[1])) {
|
|||
$languages = $config_langs;
|
||||
}
|
||||
|
||||
// Process the index file, just once.
|
||||
$keys = file_get_contents('langindex.json');
|
||||
$keys = (array) json_decode($keys);
|
||||
$keys = get_langindex_keys();
|
||||
|
||||
foreach ($keys as $key => $value) {
|
||||
$map = new StdClass();
|
||||
if ($value == 'local_moodlemobileapp') {
|
||||
$map->file = $value;
|
||||
$map->string = $key;
|
||||
} else {
|
||||
$exp = explode('/', $value, 2);
|
||||
$map->file = $exp[0];
|
||||
if (count($exp) == 2) {
|
||||
$map->string = $exp[1];
|
||||
} else {
|
||||
$exp = explode('.', $key, 3);
|
||||
|
||||
if (count($exp) == 3) {
|
||||
$map->string = $exp[2];
|
||||
} else {
|
||||
$map->string = $exp[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$keys[$key] = $map;
|
||||
}
|
||||
$total = count ($keys);
|
||||
|
||||
echo "Total strings to translate $total\n";
|
||||
|
||||
$add_langs = array();
|
||||
// Process the languages.
|
||||
foreach ($languages as $lang) {
|
||||
$ok = build_lang($lang, $keys, $total);
|
||||
if ($ok) {
|
||||
$add_langs[$lang] = $lang;
|
||||
}
|
||||
}
|
||||
$added_langs = build_languages($languages, $keys);
|
||||
|
||||
if ($forcedetect) {
|
||||
echo "\n\n\n";
|
||||
|
||||
$all_languages = glob(LANGPACKSFOLDER.'/*' , GLOB_ONLYDIR);
|
||||
function get_lang_from_dir($dir) {
|
||||
return str_replace('_', '-', explode('/', $dir)[3]);
|
||||
}
|
||||
$all_languages = array_map('get_lang_from_dir', $all_languages);
|
||||
$detect_lang = array_diff($all_languages, $languages);
|
||||
$new_langs = array();
|
||||
foreach ($detect_lang as $lang) {
|
||||
$new = detect_lang($lang, $keys, $total);
|
||||
if ($new) {
|
||||
$new_langs[$lang] = $lang;
|
||||
}
|
||||
}
|
||||
$new_langs = detect_languages($languages, $keys);
|
||||
|
||||
if (!empty($new_langs)) {
|
||||
echo "\n\n\nThe following languages are going to be added\n\n\n";
|
||||
foreach ($new_langs as $lang) {
|
||||
$ok = build_lang($lang, $keys, $total);
|
||||
if ($ok) {
|
||||
$add_langs[$lang] = $lang;
|
||||
}
|
||||
}
|
||||
add_langs_to_config($add_langs, $config);
|
||||
}
|
||||
} else {
|
||||
add_langs_to_config($add_langs, $config);
|
||||
}
|
||||
|
||||
function add_langs_to_config($langs, $config) {
|
||||
$changed = false;
|
||||
$config_langs = get_object_vars($config['languages']);
|
||||
foreach ($langs as $lang) {
|
||||
if (!isset($config_langs[$lang])) {
|
||||
$langfoldername = str_replace('-', '_', $lang);
|
||||
|
||||
$string = [];
|
||||
include(LANGPACKSFOLDER.'/'.$langfoldername.'/langconfig.php');
|
||||
$config['languages']->$lang = $string['thislanguage'];
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($changed) {
|
||||
// Sort languages by key.
|
||||
$config['languages'] = json_decode( json_encode( $config['languages'] ), true );
|
||||
ksort($config['languages']);
|
||||
$config['languages'] = json_decode( json_encode( $config['languages'] ), false );
|
||||
file_put_contents(CONFIG, json_encode($config, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
|
||||
$added_langs = build_languages($new_langs, $keys, $added_langs);
|
||||
}
|
||||
}
|
||||
|
||||
function build_lang($lang, $keys, $total) {
|
||||
$local = 0;
|
||||
$langFile = false;
|
||||
$translations = [];
|
||||
$langfoldername = str_replace('-', '_', $lang);
|
||||
|
||||
if (!is_dir(LANGPACKSFOLDER.'/'.$langfoldername) || !is_file(LANGPACKSFOLDER.'/'.$langfoldername.'/langconfig.php')) {
|
||||
echo "Cannot translate $langfoldername, folder not found";
|
||||
return false;
|
||||
}
|
||||
|
||||
$string = [];
|
||||
include(LANGPACKSFOLDER.'/'.$langfoldername.'/langconfig.php');
|
||||
$parent = isset($string['parentlanguage']) ? $string['parentlanguage'] : "";
|
||||
|
||||
echo "Processing $lang";
|
||||
if ($parent != "" && $parent != $lang) {
|
||||
echo "($parent)";
|
||||
}
|
||||
|
||||
|
||||
// Add the translation to the array.
|
||||
foreach ($keys as $key => $value) {
|
||||
$file = LANGPACKSFOLDER.'/'.$langfoldername.'/'.$value->file.'.php';
|
||||
// Apply translations.
|
||||
if (!file_exists($file)) {
|
||||
if (TOTRANSLATE) {
|
||||
echo "\n\t\To translate $value->string on $value->file";
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$string = [];
|
||||
include($file);
|
||||
|
||||
if (!isset($string[$value->string]) || ($lang == 'en' && $value->file == 'local_moodlemobileapp')) {
|
||||
// Not yet translated. Do not override.
|
||||
if (!$langFile) {
|
||||
// Load lang files just once.
|
||||
$langFile = file_get_contents(ASSETSPATH.$lang.'.json');
|
||||
$langFile = (array) json_decode($langFile);
|
||||
}
|
||||
if (is_array($langFile) && isset($langFile[$key])) {
|
||||
$translations[$key] = $langFile[$key];
|
||||
$local++;
|
||||
}
|
||||
if (TOTRANSLATE) {
|
||||
echo "\n\t\tTo translate $value->string on $value->file";
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
$text = $string[$value->string];
|
||||
}
|
||||
|
||||
if ($value->file != 'local_moodlemobileapp') {
|
||||
$text = str_replace('$a->', '$a.', $text);
|
||||
$text = str_replace('{$a', '{{$a', $text);
|
||||
$text = str_replace('}', '}}', $text);
|
||||
// Prevent double.
|
||||
$text = str_replace(array('{{{', '}}}'), array('{{', '}}'), $text);
|
||||
} else {
|
||||
$local++;
|
||||
}
|
||||
|
||||
$translations[$key] = html_entity_decode($text);
|
||||
}
|
||||
|
||||
// Sort and save.
|
||||
ksort($translations);
|
||||
file_put_contents(ASSETSPATH.$lang.'.json', str_replace('\/', '/', json_encode($translations, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)));
|
||||
|
||||
$success = count($translations);
|
||||
$percentage = floor($success/$total *100);
|
||||
echo "\t\t$success of $total -> $percentage% ($local local)\n";
|
||||
|
||||
if ($lang == 'en') {
|
||||
generate_local_moodlemobileapp($keys, $translations);
|
||||
override_component_lang_files($keys, $translations);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function detect_lang($lang, $keys, $total) {
|
||||
$success = 0;
|
||||
$local = 0;
|
||||
$langfoldername = str_replace('-', '_', $lang);
|
||||
|
||||
if (!is_dir(LANGPACKSFOLDER.'/'.$langfoldername) || !is_file(LANGPACKSFOLDER.'/'.$langfoldername.'/langconfig.php')) {
|
||||
echo "Cannot translate $langfoldername, folder not found";
|
||||
return false;
|
||||
}
|
||||
|
||||
$string = [];
|
||||
include(LANGPACKSFOLDER.'/'.$langfoldername.'/langconfig.php');
|
||||
$parent = isset($string['parentlanguage']) ? $string['parentlanguage'] : "";
|
||||
if (!isset($string['thislanguage'])) {
|
||||
echo "Cannot translate $langfoldername, name not found";
|
||||
return false;
|
||||
}
|
||||
|
||||
echo "Checking $lang";
|
||||
if ($parent != "" && $parent != $lang) {
|
||||
echo "($parent)";
|
||||
}
|
||||
$langname = $string['thislanguage'];
|
||||
echo " ".$langname." -D";
|
||||
|
||||
// Add the translation to the array.
|
||||
foreach ($keys as $key => $value) {
|
||||
$file = LANGPACKSFOLDER.'/'.$langfoldername.'/'.$value->file.'.php';
|
||||
// Apply translations.
|
||||
if (!file_exists($file)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$string = [];
|
||||
include($file);
|
||||
|
||||
if (!isset($string[$value->string])) {
|
||||
continue;
|
||||
} else {
|
||||
$text = $string[$value->string];
|
||||
}
|
||||
|
||||
if ($value->file == 'local_moodlemobileapp') {
|
||||
$local++;
|
||||
}
|
||||
|
||||
$success++;
|
||||
}
|
||||
|
||||
$percentage = floor($success/$total *100);
|
||||
echo "\t\t$success of $total -> $percentage% ($local local)";
|
||||
if (($percentage > 75 && $local > 50) || ($percentage > 50 && $local > 75)) {
|
||||
echo " \t DETECTED\n";
|
||||
return true;
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function save_key($key, $value, $path) {
|
||||
$filePath = $path . '/en.json';
|
||||
|
||||
$file = file_get_contents($filePath);
|
||||
$file = (array) json_decode($file);
|
||||
$value = html_entity_decode($value);
|
||||
if ($file[$key] != $value) {
|
||||
$file[$key] = $value;
|
||||
ksort($file);
|
||||
file_put_contents($filePath, str_replace('\/', '/', json_encode($file, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)));
|
||||
}
|
||||
}
|
||||
|
||||
function override_component_lang_files($keys, $translations) {
|
||||
echo "Override component lang files.\n";
|
||||
foreach ($translations as $key => $value) {
|
||||
$path = '../src/';
|
||||
$exp = explode('.', $key, 3);
|
||||
|
||||
$type = $exp[0];
|
||||
if (count($exp) == 3) {
|
||||
$component = $exp[1];
|
||||
$plainid = $exp[2];
|
||||
} else {
|
||||
$component = 'moodle';
|
||||
$plainid = $exp[1];
|
||||
}
|
||||
switch($type) {
|
||||
case 'core':
|
||||
case 'addon':
|
||||
switch($component) {
|
||||
case 'moodle':
|
||||
$path .= 'lang';
|
||||
break;
|
||||
default:
|
||||
$path .= $type.'/'.str_replace('_', '/', $component).'/lang';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'assets':
|
||||
$path .= $type.'/'.$component;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (is_file($path.'/en.json')) {
|
||||
save_key($plainid, $value, $path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates local moodle mobile app file to update languages in AMOS.
|
||||
*
|
||||
* @param [array] $keys Translation keys.
|
||||
* @param [array] $translations English translations.
|
||||
*/
|
||||
function generate_local_moodlemobileapp($keys, $translations) {
|
||||
echo "Generate local_moodlemobileapp.\n";
|
||||
$string = '<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Version details.
|
||||
*
|
||||
* @package local
|
||||
* @subpackage moodlemobileapp
|
||||
* @copyright 2014 Juan Leyva <juanleyvadelgado@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
$string[\'appstoredescription\'] = \'NOTE: This official Moodle Mobile app will ONLY work with Moodle sites that have been set up to allow it. Please talk to your Moodle administrator if you have any problems connecting.
|
||||
|
||||
If your Moodle site has been configured correctly, you can use this app to:
|
||||
|
||||
- browse the content of your courses, even when offline
|
||||
- receive instant notifications of messages and other events
|
||||
- quickly find and contact other people in your courses
|
||||
- upload images, audio, videos and other files from your mobile device
|
||||
- view your course grades
|
||||
- and more!
|
||||
|
||||
Please see http://docs.moodle.org/en/Mobile_app for all the latest information.
|
||||
|
||||
We’d really appreciate any good reviews about the functionality so far, and your suggestions on what else you want this app to do!
|
||||
|
||||
The app requires the following permissions:
|
||||
Record audio - For recording audio to upload to Moodle
|
||||
Read and modify the contents of your SD card - Contents are downloaded to the SD Card so you can see them offline
|
||||
Network access - To be able to connect with your Moodle site and check if you are connected or not to switch to offline mode
|
||||
Run at startup - So you receive local notifications even when the app is running in the background
|
||||
Prevent phone from sleeping - So you can receive push notifications anytime\';'."\n";
|
||||
foreach ($keys as $key => $value) {
|
||||
if (isset($translations[$key]) && $value->file == 'local_moodlemobileapp') {
|
||||
$string .= '$string[\''.$key.'\'] = \''.str_replace("'", "\'", $translations[$key]).'\';'."\n";
|
||||
}
|
||||
}
|
||||
$string .= '$string[\'pluginname\'] = \'Moodle Mobile language strings\';'."\n";
|
||||
|
||||
file_put_contents('../../moodle-local_moodlemobileapp/lang/en/local_moodlemobileapp.php', $string."\n");
|
||||
}
|
||||
|
||||
add_langs_to_config($added_langs, $config);
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
|
||||
import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
|
||||
import { CoreLoginHelperProvider } from '@core/login/providers/helper';
|
||||
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
|
||||
import { AddonBadgesProvider } from './badges';
|
||||
|
||||
/**
|
||||
|
@ -26,7 +26,7 @@ export class AddonBadgesBadgeLinkHandler extends CoreContentLinksHandlerBase {
|
|||
name = 'AddonBadgesBadgeLinkHandler';
|
||||
pattern = /\/badges\/badge\.php.*([\?\&]hash=)/;
|
||||
|
||||
constructor(private badgesProvider: AddonBadgesProvider, private loginHelper: CoreLoginHelperProvider) {
|
||||
constructor(private badgesProvider: AddonBadgesProvider, private linkHelper: CoreContentLinksHelperProvider) {
|
||||
super();
|
||||
}
|
||||
|
||||
|
@ -44,8 +44,7 @@ export class AddonBadgesBadgeLinkHandler extends CoreContentLinksHandlerBase {
|
|||
|
||||
return [{
|
||||
action: (siteId, navCtrl?): void => {
|
||||
// Always use redirect to make it the new history root (to avoid "loops" in history).
|
||||
this.loginHelper.redirect('AddonBadgesIssuedBadgePage', {courseId: 0, badgeHash: params.hash}, siteId);
|
||||
this.linkHelper.goInSite(navCtrl, 'AddonBadgesIssuedBadgePage', {courseId: 0, badgeHash: params.hash}, siteId);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
|
||||
import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
|
||||
import { CoreLoginHelperProvider } from '@core/login/providers/helper';
|
||||
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
|
||||
import { AddonBadgesProvider } from './badges';
|
||||
|
||||
/**
|
||||
|
@ -27,7 +27,7 @@ export class AddonBadgesMyBadgesLinkHandler extends CoreContentLinksHandlerBase
|
|||
featureName = 'CoreUserDelegate_AddonBadges';
|
||||
pattern = /\/badges\/mybadges\.php/;
|
||||
|
||||
constructor(private badgesProvider: AddonBadgesProvider, private loginHelper: CoreLoginHelperProvider) {
|
||||
constructor(private badgesProvider: AddonBadgesProvider, private linkHelper: CoreContentLinksHelperProvider) {
|
||||
super();
|
||||
}
|
||||
|
||||
|
@ -45,8 +45,7 @@ export class AddonBadgesMyBadgesLinkHandler extends CoreContentLinksHandlerBase
|
|||
|
||||
return [{
|
||||
action: (siteId, navCtrl?): void => {
|
||||
// Always use redirect to make it the new history root (to avoid "loops" in history).
|
||||
this.loginHelper.redirect('AddonBadgesUserBadgesPage', {}, siteId);
|
||||
this.linkHelper.goInSite(navCtrl, 'AddonBadgesUserBadgesPage', {}, siteId);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ export class AddonBlockActivityModulesHandler extends CoreBlockBaseHandler {
|
|||
* @param {number} instanceId The instance ID associated with the context level.
|
||||
* @return {CoreBlockHandlerData|Promise<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData?(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
return {
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
// (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 { CoreBlockDelegate } from '@core/block/providers/delegate';
|
||||
import { AddonBlockBadgesHandler } from './providers/block-handler';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
IonicModule,
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
exports: [
|
||||
],
|
||||
providers: [
|
||||
AddonBlockBadgesHandler
|
||||
]
|
||||
})
|
||||
export class AddonBlockBadgesModule {
|
||||
constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockBadgesHandler) {
|
||||
blockDelegate.registerHandler(blockHandler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
.addon-block-badges core-block-pre-rendered {
|
||||
.core-block-content {
|
||||
ul.badges {
|
||||
list-style: none;
|
||||
@include margin-horizontal(0);
|
||||
-webkit-padding-start: 0;
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
padding-top: 1em;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
width: 150px;
|
||||
|
||||
.badge-name {
|
||||
display: block;
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"pluginname": "Latest badges"
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// (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 { CoreBlockPreRenderedComponent } from '@core/block/components/pre-rendered-block/pre-rendered-block';
|
||||
import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler';
|
||||
|
||||
/**
|
||||
* Block handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonBlockBadgesHandler extends CoreBlockBaseHandler {
|
||||
name = 'AddonBlockBadges';
|
||||
blockName = 'badges';
|
||||
|
||||
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<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
return {
|
||||
title: 'addon.block_badges.pluginname',
|
||||
class: 'addon-block-badges',
|
||||
component: CoreBlockPreRenderedComponent
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// (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 { CoreBlockDelegate } from '@core/block/providers/delegate';
|
||||
import { AddonBlockBlogMenuHandler } from './providers/block-handler';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
IonicModule,
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
exports: [
|
||||
],
|
||||
providers: [
|
||||
AddonBlockBlogMenuHandler
|
||||
]
|
||||
})
|
||||
export class AddonBlockBlogMenuModule {
|
||||
constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockBlogMenuHandler) {
|
||||
blockDelegate.registerHandler(blockHandler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
.addon-block-blog-menu core-block-pre-rendered {
|
||||
.core-block-content {
|
||||
ul.list {
|
||||
list-style: none;
|
||||
@include margin-horizontal(0);
|
||||
-webkit-padding-start: 0;
|
||||
|
||||
li {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.core-block-footer {
|
||||
display: none;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"pluginname": "Blog menu"
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// (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 { CoreBlockPreRenderedComponent } from '@core/block/components/pre-rendered-block/pre-rendered-block';
|
||||
import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler';
|
||||
|
||||
/**
|
||||
* Block handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonBlockBlogMenuHandler extends CoreBlockBaseHandler {
|
||||
name = 'AddonBlockBlogMenu';
|
||||
blockName = 'blog_menu';
|
||||
|
||||
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<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
return {
|
||||
title: 'addon.block_blogmenu.pluginname',
|
||||
class: 'addon-block-blog-menu',
|
||||
component: CoreBlockPreRenderedComponent
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// (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 { CoreBlockDelegate } from '@core/block/providers/delegate';
|
||||
import { AddonBlockBlogRecentHandler } from './providers/block-handler';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
IonicModule,
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
exports: [
|
||||
],
|
||||
providers: [
|
||||
AddonBlockBlogRecentHandler
|
||||
]
|
||||
})
|
||||
export class AddonBlockBlogRecentModule {
|
||||
constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockBlogRecentHandler) {
|
||||
blockDelegate.registerHandler(blockHandler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
.addon-block-blog-recent core-block-pre-rendered {
|
||||
.core-block-content {
|
||||
ul.list {
|
||||
list-style: none;
|
||||
@include margin-horizontal(0);
|
||||
-webkit-padding-start: 0;
|
||||
|
||||
li {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"pluginname": "Recent blog entries"
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// (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 { CoreBlockPreRenderedComponent } from '@core/block/components/pre-rendered-block/pre-rendered-block';
|
||||
import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler';
|
||||
|
||||
/**
|
||||
* Block handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonBlockBlogRecentHandler extends CoreBlockBaseHandler {
|
||||
name = 'AddonBlockBlogRecent';
|
||||
blockName = 'blog_recent';
|
||||
|
||||
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<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
return {
|
||||
title: 'addon.block_blogrecent.pluginname',
|
||||
class: 'addon-block-blog-recent',
|
||||
component: CoreBlockPreRenderedComponent
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// (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 { CoreBlockDelegate } from '@core/block/providers/delegate';
|
||||
import { AddonBlockBlogTagsHandler } from './providers/block-handler';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
IonicModule,
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
exports: [
|
||||
],
|
||||
providers: [
|
||||
AddonBlockBlogTagsHandler
|
||||
]
|
||||
})
|
||||
export class AddonBlockBlogTagsModule {
|
||||
constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockBlogTagsHandler) {
|
||||
blockDelegate.registerHandler(blockHandler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
.addon-block-blog-tags core-block-pre-rendered {
|
||||
.core-block-content {
|
||||
ul.inline-list {
|
||||
list-style: none;
|
||||
@include margin-horizontal(0);
|
||||
-webkit-padding-start: 0;
|
||||
|
||||
li {
|
||||
padding: .2em;
|
||||
display: inline-block;
|
||||
|
||||
a {
|
||||
@extend ion-badge;
|
||||
@extend .badge-md;
|
||||
text-decoration: none;
|
||||
}
|
||||
.s20 {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.s19 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.s18 {
|
||||
font-size: 1.4em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.s17 {
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
||||
.s16 {
|
||||
font-size: 1.3em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.s15 {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.s14 {
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.s13 {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.s12,
|
||||
.s11 {
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.s10,
|
||||
.s9 {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.s8,
|
||||
.s7 {
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.s6,
|
||||
.s5 {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.s4,
|
||||
.s3 {
|
||||
font-size: 0.9em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.s2,
|
||||
.s1 {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"pluginname": "Blog tags"
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// (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 { CoreBlockPreRenderedComponent } from '@core/block/components/pre-rendered-block/pre-rendered-block';
|
||||
import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler';
|
||||
|
||||
/**
|
||||
* Block handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonBlockBlogTagsHandler extends CoreBlockBaseHandler {
|
||||
name = 'AddonBlockBlogTags';
|
||||
blockName = 'blog_tags';
|
||||
|
||||
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<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
return {
|
||||
title: 'addon.block_blogtags.pluginname',
|
||||
class: 'addon-block-blog-tags',
|
||||
component: CoreBlockPreRenderedComponent
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// (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 { CoreBlockDelegate } from '@core/block/providers/delegate';
|
||||
import { AddonBlockCalendarMonthHandler } from './providers/block-handler';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
IonicModule,
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
exports: [
|
||||
],
|
||||
providers: [
|
||||
AddonBlockCalendarMonthHandler
|
||||
]
|
||||
})
|
||||
export class AddonBlockCalendarMonthModule {
|
||||
constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockCalendarMonthHandler) {
|
||||
blockDelegate.registerHandler(blockHandler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"pluginname": "Calendar"
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
// (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 { CoreBlockOnlyTitleComponent } from '@core/block/components/only-title-block/only-title-block';
|
||||
import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler';
|
||||
import { AddonCalendarProvider } from '@addon/calendar/providers/calendar';
|
||||
|
||||
/**
|
||||
* Block handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonBlockCalendarMonthHandler extends CoreBlockBaseHandler {
|
||||
name = 'AddonBlockCalendarMonth';
|
||||
blockName = 'calendar_month';
|
||||
|
||||
constructor(private calendarProvider: AddonCalendarProvider) {
|
||||
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<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
let link = 'AddonCalendarListPage';
|
||||
const linkParams: any = contextLevel == 'course' ? { courseId: instanceId } : {};
|
||||
|
||||
if (this.calendarProvider.canViewMonthInSite()) {
|
||||
link = 'AddonCalendarIndexPage';
|
||||
}
|
||||
|
||||
return {
|
||||
title: 'addon.block_calendarmonth.pluginname',
|
||||
class: 'addon-block-calendar-month',
|
||||
component: CoreBlockOnlyTitleComponent,
|
||||
link: link,
|
||||
linkParams: linkParams
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// (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 { CoreBlockDelegate } from '@core/block/providers/delegate';
|
||||
import { AddonBlockCalendarUpcomingHandler } from './providers/block-handler';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
IonicModule,
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
exports: [
|
||||
],
|
||||
providers: [
|
||||
AddonBlockCalendarUpcomingHandler
|
||||
]
|
||||
})
|
||||
export class AddonBlockCalendarUpcomingModule {
|
||||
constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockCalendarUpcomingHandler) {
|
||||
blockDelegate.registerHandler(blockHandler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"pluginname": " Upcoming events"
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
// (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 { CoreBlockOnlyTitleComponent } from '@core/block/components/only-title-block/only-title-block';
|
||||
import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler';
|
||||
import { AddonCalendarProvider } from '@addon/calendar/providers/calendar';
|
||||
|
||||
/**
|
||||
* Block handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonBlockCalendarUpcomingHandler extends CoreBlockBaseHandler {
|
||||
name = 'AddonBlockCalendarUpcoming';
|
||||
blockName = 'calendar_upcoming';
|
||||
|
||||
constructor(private calendarProvider: AddonCalendarProvider) {
|
||||
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<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
let link = 'AddonCalendarListPage';
|
||||
const linkParams: any = contextLevel == 'course' ? { courseId: instanceId } : {};
|
||||
|
||||
if (this.calendarProvider.canViewMonthInSite()) {
|
||||
link = 'AddonCalendarIndexPage';
|
||||
linkParams.upcoming = true;
|
||||
}
|
||||
|
||||
return {
|
||||
title: 'addon.block_calendarupcoming.pluginname',
|
||||
class: 'addon-block-calendar-upcoming',
|
||||
component: CoreBlockOnlyTitleComponent,
|
||||
link: link,
|
||||
linkParams: linkParams
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// (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 { CoreBlockDelegate } from '@core/block/providers/delegate';
|
||||
import { AddonBlockCommentsHandler } from './providers/block-handler';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
IonicModule,
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
exports: [
|
||||
],
|
||||
providers: [
|
||||
AddonBlockCommentsHandler
|
||||
]
|
||||
})
|
||||
export class AddonBlockCommentsModule {
|
||||
constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockCommentsHandler) {
|
||||
blockDelegate.registerHandler(blockHandler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"pluginname": "Comments"
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
// (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 { CoreBlockOnlyTitleComponent } from '@core/block/components/only-title-block/only-title-block';
|
||||
import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler';
|
||||
|
||||
/**
|
||||
* Block handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonBlockCommentsHandler extends CoreBlockBaseHandler {
|
||||
name = 'AddonBlockComments';
|
||||
blockName = 'comments';
|
||||
|
||||
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<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
return {
|
||||
title: 'addon.block_comments.pluginname',
|
||||
class: 'addon-block-comments',
|
||||
component: CoreBlockOnlyTitleComponent,
|
||||
link: 'CoreCommentsViewerPage',
|
||||
linkParams: { contextLevel: contextLevel, instanceId: instanceId,
|
||||
componentName: 'block_comments', area: 'page_comments', itemId: 0 }
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// (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 { CoreBlockDelegate } from '@core/block/providers/delegate';
|
||||
import { AddonBlockCompletionStatusHandler } from './providers/block-handler';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
IonicModule,
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
exports: [
|
||||
],
|
||||
providers: [
|
||||
AddonBlockCompletionStatusHandler
|
||||
]
|
||||
})
|
||||
export class AddonBlockCompletionStatusModule {
|
||||
constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockCompletionStatusHandler) {
|
||||
blockDelegate.registerHandler(blockHandler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"pluginname": "Course completion status"
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
// (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 { CoreBlockOnlyTitleComponent } from '@core/block/components/only-title-block/only-title-block';
|
||||
import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler';
|
||||
|
||||
/**
|
||||
* Block handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonBlockCompletionStatusHandler extends CoreBlockBaseHandler {
|
||||
name = 'AddonBlockCompletionStatus';
|
||||
blockName = 'completionstatus';
|
||||
|
||||
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<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
return {
|
||||
title: 'addon.block_completionstatus.pluginname',
|
||||
class: 'addon-block-completion-status',
|
||||
component: CoreBlockOnlyTitleComponent,
|
||||
link: 'AddonCourseCompletionReportPage',
|
||||
linkParams: { courseId: instanceId }
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// (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 { CoreBlockDelegate } from '@core/block/providers/delegate';
|
||||
import { AddonBlockGlossaryRandomHandler } from './providers/block-handler';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
IonicModule,
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
exports: [
|
||||
],
|
||||
providers: [
|
||||
AddonBlockGlossaryRandomHandler
|
||||
]
|
||||
})
|
||||
export class AddonBlockGlossaryRandomModule {
|
||||
constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockGlossaryRandomHandler) {
|
||||
blockDelegate.registerHandler(blockHandler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"pluginname": "Random glossary entry"
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
// (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 { CoreBlockPreRenderedComponent } from '@core/block/components/pre-rendered-block/pre-rendered-block';
|
||||
import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler';
|
||||
|
||||
/**
|
||||
* Block handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonBlockGlossaryRandomHandler extends CoreBlockBaseHandler {
|
||||
name = 'AddonBlockGlossaryRandom';
|
||||
blockName = 'glossary_random';
|
||||
|
||||
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<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
return {
|
||||
title: block.contents.title || 'addon.block_glossaryrandom.pluginname',
|
||||
class: 'addon-block-glossary-random',
|
||||
component: CoreBlockPreRenderedComponent
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
// (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 { CoreBlockDelegate } from '@core/block/providers/delegate';
|
||||
import { AddonBlockHtmlHandler } from './providers/block-handler';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
IonicModule
|
||||
],
|
||||
exports: [
|
||||
],
|
||||
providers: [
|
||||
AddonBlockHtmlHandler
|
||||
]
|
||||
})
|
||||
export class AddonBlockHtmlModule {
|
||||
constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockHtmlHandler) {
|
||||
blockDelegate.registerHandler(blockHandler);
|
||||
}
|
||||
}
|
|
@ -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 { CoreBlockPreRenderedComponent } from '@core/block/components/pre-rendered-block/pre-rendered-block';
|
||||
import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler';
|
||||
|
||||
/**
|
||||
* Block handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonBlockHtmlHandler extends CoreBlockBaseHandler {
|
||||
name = 'AddonBlockHtml';
|
||||
blockName = 'html';
|
||||
|
||||
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<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
return {
|
||||
title: block.contents.title,
|
||||
class: 'addon-block-html',
|
||||
component: CoreBlockPreRenderedComponent
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"pluginname": "Learning plans"
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
// (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 { CoreBlockDelegate } from '@core/block/providers/delegate';
|
||||
import { AddonBlockLearningPlansHandler } from './providers/block-handler';
|
||||
import { CoreBlockComponentsModule } from '@core/block/components/components.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
IonicModule,
|
||||
CoreBlockComponentsModule,
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
exports: [
|
||||
],
|
||||
providers: [
|
||||
AddonBlockLearningPlansHandler
|
||||
]
|
||||
})
|
||||
export class AddonBlockLearningPlansModule {
|
||||
constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockLearningPlansHandler) {
|
||||
blockDelegate.registerHandler(blockHandler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// (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 { CoreBlockOnlyTitleComponent } from '@core/block/components/only-title-block/only-title-block';
|
||||
import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler';
|
||||
|
||||
/**
|
||||
* Block handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonBlockLearningPlansHandler extends CoreBlockBaseHandler {
|
||||
name = 'AddonBlockLearningPlans';
|
||||
blockName = 'lp';
|
||||
|
||||
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<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
return {
|
||||
title: 'addon.block_learningplans.pluginname',
|
||||
class: 'addon-block-learning-plans',
|
||||
component: CoreBlockOnlyTitleComponent,
|
||||
link: 'AddonCompetencyPlanListPage'
|
||||
};
|
||||
}
|
||||
}
|
|
@ -19,11 +19,11 @@
|
|||
<!-- "Time" selector. -->
|
||||
<ion-select text-start [title]="'core.show' | translate" [(ngModel)]="selectedFilter" (ngModelChange)="selectedChanged()" interface="popover" class="core-button-select">
|
||||
<ion-option value="all">{{ 'addon.block_myoverview.all' | translate }}</ion-option>∫
|
||||
<ion-option value="inprogress">{{ 'addon.block_myoverview.inprogress' | translate }}</ion-option>
|
||||
<ion-option value="future">{{ 'addon.block_myoverview.future' | translate }}</ion-option>
|
||||
<ion-option value="past">{{ 'addon.block_myoverview.past' | translate }}</ion-option>
|
||||
<ion-option value="favourite" *ngIf="showFavourite">{{ 'addon.block_myoverview.favourites' | translate }}</ion-option>
|
||||
<ion-option value="hidden" *ngIf="showHidden">{{ 'addon.block_myoverview.hiddencourses' | translate }}</ion-option>
|
||||
<ion-option value="inprogress" [disabled]="disableInProgress">{{ 'addon.block_myoverview.inprogress' | translate }}</ion-option>
|
||||
<ion-option value="future" [disabled]="disableFuture">{{ 'addon.block_myoverview.future' | translate }}</ion-option>
|
||||
<ion-option value="past" [disabled]="disablePast">{{ 'addon.block_myoverview.past' | translate }}</ion-option>
|
||||
<ion-option value="favourite" *ngIf="showFavourite" [disabled]="disableFavourite">{{ 'addon.block_myoverview.favourites' | translate }}</ion-option>
|
||||
<ion-option value="hidden" *ngIf="showHidden" [disabled]="disableHidden">{{ 'addon.block_myoverview.hiddencourses' | translate }}</ion-option>
|
||||
</ion-select>
|
||||
</div>
|
||||
<core-empty-box *ngIf="courses[selectedFilter].length == 0" image="assets/img/icons/courses.svg" [message]="'addon.block_myoverview.nocourses' | translate"></core-empty-box>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, OnInit, Input, OnDestroy, ViewChild, Injector } from '@angular/core';
|
||||
import { Component, OnInit, Input, OnDestroy, ViewChild, Injector, OnChanges, SimpleChange } from '@angular/core';
|
||||
import { Searchbar } from 'ionic-angular';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
|
@ -32,7 +32,7 @@ import { CoreBlockBaseComponent } from '@core/block/classes/base-block-component
|
|||
selector: 'addon-block-myoverview',
|
||||
templateUrl: 'addon-block-myoverview.html'
|
||||
})
|
||||
export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implements OnInit, OnDestroy {
|
||||
export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implements OnInit, OnChanges, OnDestroy {
|
||||
@ViewChild('searchbar') searchbar: Searchbar;
|
||||
@Input() downloadEnabled: boolean;
|
||||
|
||||
|
@ -64,10 +64,14 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
|||
showSortFilter = false;
|
||||
downloadCourseEnabled: boolean;
|
||||
downloadCoursesEnabled: boolean;
|
||||
disableInProgress = false;
|
||||
disablePast = false;
|
||||
disableFuture = false;
|
||||
disableFavourite = false;
|
||||
disableHidden = false;
|
||||
|
||||
protected prefetchIconsInitialized = false;
|
||||
protected isDestroyed;
|
||||
protected downloadButtonObserver;
|
||||
protected coursesObserver;
|
||||
protected updateSiteObserver;
|
||||
protected courseIds = [];
|
||||
|
@ -87,18 +91,6 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
|||
*/
|
||||
ngOnInit(): void {
|
||||
// Refresh the enabled flags if enabled.
|
||||
this.downloadButtonObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_DASHBOARD_DOWNLOAD_ENABLED_CHANGED,
|
||||
(data) => {
|
||||
const wasEnabled = this.downloadEnabled;
|
||||
|
||||
this.downloadEnabled = data.enabled;
|
||||
|
||||
if (!wasEnabled && this.downloadEnabled && this.loaded) {
|
||||
// Download all courses is enabled now, initialize it.
|
||||
this.initPrefetchCoursesIcons();
|
||||
}
|
||||
});
|
||||
|
||||
this.downloadCourseEnabled = !this.coursesProvider.isDownloadCourseDisabledInSite();
|
||||
this.downloadCoursesEnabled = !this.coursesProvider.isDownloadCoursesDisabledInSite();
|
||||
|
||||
|
@ -128,6 +120,16 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect changes on input properties.
|
||||
*/
|
||||
ngOnChanges(changes: {[name: string]: SimpleChange}): void {
|
||||
if (changes.downloadEnabled && !changes.downloadEnabled.previousValue && this.downloadEnabled && this.loaded) {
|
||||
// Download all courses is enabled now, initialize it.
|
||||
this.initPrefetchCoursesIcons();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the invalidate content function.
|
||||
*
|
||||
|
@ -173,12 +175,17 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
|||
|
||||
this.courses.filter = '';
|
||||
this.showFilter = false;
|
||||
this.disableInProgress = this.courses.inprogress.length === 0;
|
||||
this.disablePast = this.courses.past.length === 0;
|
||||
this.disableFuture = this.courses.future.length === 0;
|
||||
this.showSelectorFilter = courses.length > 0 && (this.courses.past.length > 0 || this.courses.future.length > 0 ||
|
||||
typeof courses[0].enddate != 'undefined');
|
||||
typeof courses[0].enddate != 'undefined');
|
||||
this.showHidden = this.showSelectorFilter && typeof courses[0].hidden != 'undefined';
|
||||
this.disableHidden = this.courses.hidden.length === 0;
|
||||
this.showFavourite = this.showSelectorFilter && typeof courses[0].isfavourite != 'undefined';
|
||||
if (!this.showSelectorFilter) {
|
||||
// No selector, show all.
|
||||
this.disableFavourite = this.courses.favourite.length === 0;
|
||||
if (!this.showSelectorFilter || (this.selectedFilter === 'inprogress' && this.disableInProgress)) {
|
||||
// No selector, or the default option is disabled, show all.
|
||||
this.selectedFilter = 'all';
|
||||
}
|
||||
this.filteredCourses = this.courses[this.selectedFilter];
|
||||
|
@ -350,6 +357,5 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
|||
this.isDestroyed = true;
|
||||
this.coursesObserver && this.coursesObserver.off();
|
||||
this.updateSiteObserver && this.updateSiteObserver.off();
|
||||
this.downloadButtonObserver && this.downloadButtonObserver.off();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ export class AddonBlockMyOverviewHandler extends CoreBlockBaseHandler {
|
|||
* @param {number} instanceId The instance ID associated with the context level.
|
||||
* @return {CoreBlockHandlerData|Promise<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData?(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
return {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"pluginname": "Latest announcements"
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// (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 { CoreBlockDelegate } from '@core/block/providers/delegate';
|
||||
import { AddonBlockNewsItemsHandler } from './providers/block-handler';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
IonicModule,
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
exports: [
|
||||
],
|
||||
providers: [
|
||||
AddonBlockNewsItemsHandler
|
||||
]
|
||||
})
|
||||
export class AddonBlockNewsItemsModule {
|
||||
constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockNewsItemsHandler) {
|
||||
blockDelegate.registerHandler(blockHandler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
.addon-block-news-items core-block-pre-rendered {
|
||||
.core-block-content {
|
||||
.unlist {
|
||||
list-style-type: none;
|
||||
@include margin-horizontal(0);
|
||||
-webkit-padding-start: 0;
|
||||
|
||||
li.post {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
li.post:last-child {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hide RSS link.
|
||||
.core-block-footer {
|
||||
a {
|
||||
display: none;
|
||||
}
|
||||
a:first-child {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
// (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 { CoreBlockPreRenderedComponent } from '@core/block/components/pre-rendered-block/pre-rendered-block';
|
||||
import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler';
|
||||
|
||||
/**
|
||||
* Block handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonBlockNewsItemsHandler extends CoreBlockBaseHandler {
|
||||
name = 'AddonBlockNewsItems';
|
||||
blockName = 'news_items';
|
||||
|
||||
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<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
return {
|
||||
title: 'addon.block_newsitems.pluginname',
|
||||
class: 'addon-block-news-items',
|
||||
component: CoreBlockPreRenderedComponent
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"pluginname": "Online users"
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// (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 { CoreBlockDelegate } from '@core/block/providers/delegate';
|
||||
import { AddonBlockOnlineUsersHandler } from './providers/block-handler';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
IonicModule,
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
exports: [
|
||||
],
|
||||
providers: [
|
||||
AddonBlockOnlineUsersHandler
|
||||
]
|
||||
})
|
||||
export class AddonBlockOnlineUsersModule {
|
||||
constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockOnlineUsersHandler) {
|
||||
blockDelegate.registerHandler(blockHandler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
.addon-block-online-users core-block-pre-rendered .core-block-content {
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
.item-inner,
|
||||
.input-wrapper {
|
||||
overflow-y: visible;
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
.list {
|
||||
@include margin-horizontal(0);
|
||||
-webkit-padding-start: 0;
|
||||
|
||||
li.listentry {
|
||||
clear: both;
|
||||
list-style-type: none;
|
||||
|
||||
.user {
|
||||
@include float(start);
|
||||
position: relative;
|
||||
padding-bottom: 16px;
|
||||
|
||||
.core-adapted-img-container {
|
||||
display: inline;
|
||||
@include margin-horizontal(0, 8px);
|
||||
}
|
||||
|
||||
.userpicture {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
}
|
||||
|
||||
.message {
|
||||
@include float(end);
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.uservisibility { // No support on the app.
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
// (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 { CoreBlockPreRenderedComponent } from '@core/block/components/pre-rendered-block/pre-rendered-block';
|
||||
import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler';
|
||||
|
||||
/**
|
||||
* Block handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonBlockOnlineUsersHandler extends CoreBlockBaseHandler {
|
||||
name = 'AddonBlockOnlineUsers';
|
||||
blockName = 'online_users';
|
||||
|
||||
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<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
return {
|
||||
title: 'addon.block_onlineusers.pluginname',
|
||||
class: 'addon-block-online-users',
|
||||
component: CoreBlockPreRenderedComponent
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"pluginname": "Private files"
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// (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 { CoreBlockDelegate } from '@core/block/providers/delegate';
|
||||
import { AddonBlockPrivateFilesHandler } from './providers/block-handler';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
IonicModule,
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
exports: [
|
||||
],
|
||||
providers: [
|
||||
AddonBlockPrivateFilesHandler
|
||||
]
|
||||
})
|
||||
export class AddonBlockPrivateFilesModule {
|
||||
constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockPrivateFilesHandler) {
|
||||
blockDelegate.registerHandler(blockHandler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
// (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 { CoreBlockOnlyTitleComponent } from '@core/block/components/only-title-block/only-title-block';
|
||||
import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler';
|
||||
|
||||
/**
|
||||
* Block handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonBlockPrivateFilesHandler extends CoreBlockBaseHandler {
|
||||
name = 'AddonBlockPrivateFiles';
|
||||
blockName = 'private_files';
|
||||
|
||||
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<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
return {
|
||||
title: 'addon.block_privatefiles.pluginname',
|
||||
class: 'addon-block-private-files',
|
||||
component: CoreBlockOnlyTitleComponent,
|
||||
link: 'AddonFilesListPage',
|
||||
linkParams: {root: 'my'}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"pluginname": "Recent activity"
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// (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 { CoreBlockPreRenderedComponent } from '@core/block/components/pre-rendered-block/pre-rendered-block';
|
||||
import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler';
|
||||
|
||||
/**
|
||||
* Block handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonBlockRecentActivityHandler extends CoreBlockBaseHandler {
|
||||
name = 'AddonBlockRecentActivity';
|
||||
blockName = 'recent_activity';
|
||||
|
||||
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<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
return {
|
||||
title: 'addon.block_recentactivity.pluginname',
|
||||
class: 'addon-block-recent-activity',
|
||||
component: CoreBlockPreRenderedComponent
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// (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 { CoreBlockDelegate } from '@core/block/providers/delegate';
|
||||
import { AddonBlockRecentActivityHandler } from './providers/block-handler';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
IonicModule,
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
exports: [
|
||||
],
|
||||
providers: [
|
||||
AddonBlockRecentActivityHandler
|
||||
]
|
||||
})
|
||||
export class AddonBlockRecentActivityModule {
|
||||
constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockRecentActivityHandler) {
|
||||
blockDelegate.registerHandler(blockHandler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
.addon-block-recent-activity core-block-pre-rendered {
|
||||
.core-block-content {
|
||||
.activitydate, .activityhead {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.unlist {
|
||||
list-style: none;
|
||||
@include margin-horizontal(0);
|
||||
-webkit-padding-start: 0;
|
||||
li {
|
||||
margin-bottom: 1em;
|
||||
|
||||
.head .date {
|
||||
@include float(end);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, OnInit, OnDestroy, Injector, Input } from '@angular/core';
|
||||
import { Component, OnInit, OnDestroy, Injector, Input, OnChanges, SimpleChange } from '@angular/core';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
|
@ -30,7 +30,7 @@ import { CoreBlockBaseComponent } from '@core/block/classes/base-block-component
|
|||
selector: 'addon-block-recentlyaccessedcourses',
|
||||
templateUrl: 'addon-block-recentlyaccessedcourses.html'
|
||||
})
|
||||
export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseComponent implements OnInit, OnDestroy {
|
||||
export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseComponent implements OnInit, OnChanges, OnDestroy {
|
||||
@Input() downloadEnabled: boolean;
|
||||
|
||||
courses = [];
|
||||
|
@ -41,7 +41,6 @@ export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseCom
|
|||
|
||||
protected prefetchIconsInitialized = false;
|
||||
protected isDestroyed;
|
||||
protected downloadButtonObserver;
|
||||
protected coursesObserver;
|
||||
protected courseIds = [];
|
||||
protected fetchContentDefaultError = 'Error getting recent courses data.';
|
||||
|
@ -59,18 +58,6 @@ export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseCom
|
|||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
// Refresh the enabled flags if enabled.
|
||||
this.downloadButtonObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_DASHBOARD_DOWNLOAD_ENABLED_CHANGED,
|
||||
(data) => {
|
||||
const wasEnabled = this.downloadEnabled;
|
||||
|
||||
this.downloadEnabled = data.enabled;
|
||||
|
||||
if (!wasEnabled && this.downloadEnabled && this.loaded) {
|
||||
// Download all courses is enabled now, initialize it.
|
||||
this.initPrefetchCoursesIcons();
|
||||
}
|
||||
});
|
||||
|
||||
this.coursesObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, () => {
|
||||
this.refreshContent();
|
||||
|
@ -79,6 +66,16 @@ export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseCom
|
|||
super.ngOnInit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect changes on input properties.
|
||||
*/
|
||||
ngOnChanges(changes: {[name: string]: SimpleChange}): void {
|
||||
if (changes.downloadEnabled && !changes.downloadEnabled.previousValue && this.downloadEnabled && this.loaded) {
|
||||
// Download all courses is enabled now, initialize it.
|
||||
this.initPrefetchCoursesIcons();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the invalidate content function.
|
||||
*
|
||||
|
@ -155,6 +152,5 @@ export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseCom
|
|||
ngOnDestroy(): void {
|
||||
this.isDestroyed = true;
|
||||
this.coursesObserver && this.coursesObserver.off();
|
||||
this.downloadButtonObserver && this.downloadButtonObserver.off();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ export class AddonBlockRecentlyAccessedCoursesHandler extends CoreBlockBaseHandl
|
|||
* @param {number} instanceId The instance ID associated with the context level.
|
||||
* @return {CoreBlockHandlerData|Promise<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData?(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
return {
|
||||
|
|
|
@ -38,7 +38,7 @@ export class AddonBlockRecentlyAccessedItemsHandler extends CoreBlockBaseHandler
|
|||
* @param {number} instanceId The instance ID associated with the context level.
|
||||
* @return {CoreBlockHandlerData|Promise<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData?(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
return {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"pluginname": "Remote RSS feeds"
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// (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 { CoreBlockPreRenderedComponent } from '@core/block/components/pre-rendered-block/pre-rendered-block';
|
||||
import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler';
|
||||
|
||||
/**
|
||||
* Block handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonBlockRssClientHandler extends CoreBlockBaseHandler {
|
||||
name = 'AddonBlockRssClient';
|
||||
blockName = 'rss_client';
|
||||
|
||||
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<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
return {
|
||||
title: block.contents.title || 'addon.block_rssclient.pluginname',
|
||||
class: 'addon-block-rss-client',
|
||||
component: CoreBlockPreRenderedComponent
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// (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 { CoreBlockDelegate } from '@core/block/providers/delegate';
|
||||
import { AddonBlockRssClientHandler } from './providers/block-handler';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
IonicModule,
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
exports: [
|
||||
],
|
||||
providers: [
|
||||
AddonBlockRssClientHandler
|
||||
]
|
||||
})
|
||||
export class AddonBlockRssClientModule {
|
||||
constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockRssClientHandler) {
|
||||
blockDelegate.registerHandler(blockHandler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
.addon-block-rss-client core-block-pre-rendered {
|
||||
.core-block-content {
|
||||
.list {
|
||||
list-style: none;
|
||||
@include margin-horizontal(0);
|
||||
-webkit-padding-start: 0;
|
||||
|
||||
li {
|
||||
border-top: 1px solid $gray;
|
||||
padding: 5px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
li:first-child {
|
||||
border-top-width: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"pluginname": "Self completion"
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
// (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 { CoreBlockOnlyTitleComponent } from '@core/block/components/only-title-block/only-title-block';
|
||||
import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler';
|
||||
|
||||
/**
|
||||
* Block handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonBlockSelfCompletionHandler extends CoreBlockBaseHandler {
|
||||
name = 'AddonBlockSelfCompletion';
|
||||
blockName = 'selfcompletion';
|
||||
|
||||
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<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
return {
|
||||
title: 'addon.block_selfcompletion.pluginname',
|
||||
class: 'addon-block-self-completion',
|
||||
component: CoreBlockOnlyTitleComponent,
|
||||
link: 'AddonCourseCompletionReportPage',
|
||||
linkParams: { courseId: instanceId }
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// (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 { CoreBlockDelegate } from '@core/block/providers/delegate';
|
||||
import { AddonBlockSelfCompletionHandler } from './providers/block-handler';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
IonicModule,
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
exports: [
|
||||
],
|
||||
providers: [
|
||||
AddonBlockSelfCompletionHandler
|
||||
]
|
||||
})
|
||||
export class AddonBlockSelfCompletionModule {
|
||||
constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockSelfCompletionHandler) {
|
||||
blockDelegate.registerHandler(blockHandler);
|
||||
}
|
||||
}
|
|
@ -2,9 +2,11 @@
|
|||
<h2>{{ 'addon.block_sitemainmenu.pluginname' | translate }}</h2>
|
||||
</ion-item-divider>
|
||||
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
||||
<ion-item text-wrap *ngIf="block.summary">
|
||||
<core-format-text [text]="block.summary"></core-format-text>
|
||||
</ion-item>
|
||||
<ng-container *ngIf="mainMenuBlock">
|
||||
<ion-item text-wrap *ngIf="mainMenuBlock.summary">
|
||||
<core-format-text [text]="mainMenuBlock.summary"></core-format-text>
|
||||
</ion-item>
|
||||
|
||||
<core-course-module *ngFor="let module of block.modules" [module]="module" [courseId]="siteHomeId" [downloadEnabled]="true" [section]="block"></core-course-module>
|
||||
<core-course-module *ngFor="let module of mainMenuBlock.modules" [module]="module" [courseId]="siteHomeId" [downloadEnabled]="downloadEnabled" [section]="mainMenuBlock"></core-course-module>
|
||||
</ng-container>
|
||||
</core-loading>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, OnInit, Injector } from '@angular/core';
|
||||
import { Component, OnInit, Injector, Input } from '@angular/core';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||
import { CoreCourseHelperProvider } from '@core/course/providers/helper';
|
||||
|
@ -28,7 +28,9 @@ import { CoreBlockBaseComponent } from '@core/block/classes/base-block-component
|
|||
templateUrl: 'addon-block-sitemainmenu.html'
|
||||
})
|
||||
export class AddonBlockSiteMainMenuComponent extends CoreBlockBaseComponent implements OnInit {
|
||||
block: any;
|
||||
@Input() downloadEnabled: boolean;
|
||||
|
||||
mainMenuBlock: any;
|
||||
siteHomeId: number;
|
||||
|
||||
protected fetchContentDefaultError = 'Error getting main menu data.';
|
||||
|
@ -60,9 +62,9 @@ export class AddonBlockSiteMainMenuComponent extends CoreBlockBaseComponent impl
|
|||
promises.push(this.courseProvider.invalidateSections(this.siteHomeId));
|
||||
promises.push(this.siteHomeProvider.invalidateNewsForum(this.siteHomeId));
|
||||
|
||||
if (this.block && this.block.modules) {
|
||||
if (this.mainMenuBlock && this.mainMenuBlock.modules) {
|
||||
// Invalidate modules prefetch data.
|
||||
promises.push(this.prefetchDelegate.invalidateModules(this.block.modules, this.siteHomeId));
|
||||
promises.push(this.prefetchDelegate.invalidateModules(this.mainMenuBlock.modules, this.siteHomeId));
|
||||
}
|
||||
|
||||
return Promise.all(promises);
|
||||
|
@ -75,11 +77,11 @@ export class AddonBlockSiteMainMenuComponent extends CoreBlockBaseComponent impl
|
|||
*/
|
||||
protected fetchContent(): Promise<any> {
|
||||
return this.courseProvider.getSections(this.siteHomeId, false, true).then((sections) => {
|
||||
this.block = sections.find((section) => section.section == 0);
|
||||
this.mainMenuBlock = sections.find((section) => section.section == 0);
|
||||
|
||||
if (this.block) {
|
||||
this.block.hasContent = this.courseHelper.sectionHasContent(this.block);
|
||||
this.courseHelper.addHandlerDataForModules([this.block], this.siteHomeId);
|
||||
if (this.mainMenuBlock) {
|
||||
this.mainMenuBlock.hasContent = this.courseHelper.sectionHasContent(this.mainMenuBlock);
|
||||
this.courseHelper.addHandlerDataForModules([this.mainMenuBlock], this.siteHomeId);
|
||||
|
||||
// Check if Site Home displays announcements. If so, remove it from the main menu block.
|
||||
const currentSite = this.sitesProvider.getCurrentSite(),
|
||||
|
@ -92,15 +94,15 @@ export class AddonBlockSiteMainMenuComponent extends CoreBlockBaseComponent impl
|
|||
hasNewsItem = items.find((item) => { return item == '0'; });
|
||||
}
|
||||
|
||||
if (hasNewsItem && this.block.modules) {
|
||||
if (hasNewsItem && this.mainMenuBlock.modules) {
|
||||
// Remove forum activity (news one only) from the main menu block to prevent duplicates.
|
||||
return this.siteHomeProvider.getNewsForum(this.siteHomeId).then((forum) => {
|
||||
// Search the module that belongs to site news.
|
||||
for (let i = 0; i < this.block.modules.length; i++) {
|
||||
const module = this.block.modules[i];
|
||||
for (let i = 0; i < this.mainMenuBlock.modules.length; i++) {
|
||||
const module = this.mainMenuBlock.modules[i];
|
||||
|
||||
if (module.modname == 'forum' && module.instance == forum.id) {
|
||||
this.block.modules.splice(i, 1);
|
||||
this.mainMenuBlock.modules.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ export class AddonBlockSiteMainMenuHandler extends CoreBlockBaseHandler {
|
|||
* @param {number} instanceId The instance ID associated with the context level.
|
||||
* @return {CoreBlockHandlerData|Promise<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData?(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
return {
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, OnInit, OnDestroy, Injector, Input } from '@angular/core';
|
||||
import { Component, OnInit, OnDestroy, Injector, Input, OnChanges, SimpleChange } from '@angular/core';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
|
@ -30,7 +30,7 @@ import { CoreBlockBaseComponent } from '@core/block/classes/base-block-component
|
|||
selector: 'addon-block-starredcourses',
|
||||
templateUrl: 'addon-block-starredcourses.html'
|
||||
})
|
||||
export class AddonBlockStarredCoursesComponent extends CoreBlockBaseComponent implements OnInit, OnDestroy {
|
||||
export class AddonBlockStarredCoursesComponent extends CoreBlockBaseComponent implements OnInit, OnChanges, OnDestroy {
|
||||
@Input() downloadEnabled: boolean;
|
||||
|
||||
courses = [];
|
||||
|
@ -41,7 +41,6 @@ export class AddonBlockStarredCoursesComponent extends CoreBlockBaseComponent im
|
|||
|
||||
protected prefetchIconsInitialized = false;
|
||||
protected isDestroyed;
|
||||
protected downloadButtonObserver;
|
||||
protected coursesObserver;
|
||||
protected courseIds = [];
|
||||
protected fetchContentDefaultError = 'Error getting starred courses data.';
|
||||
|
@ -59,18 +58,6 @@ export class AddonBlockStarredCoursesComponent extends CoreBlockBaseComponent im
|
|||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
// Refresh the enabled flags if enabled.
|
||||
this.downloadButtonObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_DASHBOARD_DOWNLOAD_ENABLED_CHANGED,
|
||||
(data) => {
|
||||
const wasEnabled = this.downloadEnabled;
|
||||
|
||||
this.downloadEnabled = data.enabled;
|
||||
|
||||
if (!wasEnabled && this.downloadEnabled && this.loaded) {
|
||||
// Download all courses is enabled now, initialize it.
|
||||
this.initPrefetchCoursesIcons();
|
||||
}
|
||||
});
|
||||
|
||||
this.coursesObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, () => {
|
||||
this.refreshContent();
|
||||
|
@ -79,6 +66,16 @@ export class AddonBlockStarredCoursesComponent extends CoreBlockBaseComponent im
|
|||
super.ngOnInit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect changes on input properties.
|
||||
*/
|
||||
ngOnChanges(changes: {[name: string]: SimpleChange}): void {
|
||||
if (changes.downloadEnabled && !changes.downloadEnabled.previousValue && this.downloadEnabled && this.loaded) {
|
||||
// Download all courses is enabled now, initialize it.
|
||||
this.initPrefetchCoursesIcons();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the invalidate content function.
|
||||
*
|
||||
|
@ -155,6 +152,5 @@ export class AddonBlockStarredCoursesComponent extends CoreBlockBaseComponent im
|
|||
ngOnDestroy(): void {
|
||||
this.isDestroyed = true;
|
||||
this.coursesObserver && this.coursesObserver.off();
|
||||
this.downloadButtonObserver && this.downloadButtonObserver.off();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ export class AddonBlockStarredCoursesHandler extends CoreBlockBaseHandler {
|
|||
* @param {number} instanceId The instance ID associated with the context level.
|
||||
* @return {CoreBlockHandlerData|Promise<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData?(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
return {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"pluginname": "Tags"
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// (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 { CoreBlockPreRenderedComponent } from '@core/block/components/pre-rendered-block/pre-rendered-block';
|
||||
import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler';
|
||||
|
||||
/**
|
||||
* Block handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonBlockTagsHandler extends CoreBlockBaseHandler {
|
||||
name = 'AddonBlockTags';
|
||||
blockName = 'tags';
|
||||
|
||||
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<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
return {
|
||||
title: 'addon.block_tags.pluginname',
|
||||
class: 'addon-block-tags',
|
||||
component: CoreBlockPreRenderedComponent
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// (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 { CoreBlockDelegate } from '@core/block/providers/delegate';
|
||||
import { AddonBlockTagsHandler } from './providers/block-handler';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
IonicModule,
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
exports: [
|
||||
],
|
||||
providers: [
|
||||
AddonBlockTagsHandler
|
||||
]
|
||||
})
|
||||
export class AddonBlockTagsModule {
|
||||
constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockTagsHandler) {
|
||||
blockDelegate.registerHandler(blockHandler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
.addon-block-tags core-block-pre-rendered {
|
||||
.core-block-content {
|
||||
.tag_cloud {
|
||||
text-align: center;
|
||||
ul.inline-list {
|
||||
list-style: none;
|
||||
@include margin-horizontal(0);
|
||||
-webkit-padding-start: 0;
|
||||
|
||||
li {
|
||||
padding: .2em;
|
||||
display: inline-block;
|
||||
|
||||
a {
|
||||
@extend ion-badge;
|
||||
@extend .badge-md;
|
||||
text-decoration: none;
|
||||
}
|
||||
.s20 {
|
||||
font-size: 2.7em;
|
||||
}
|
||||
|
||||
.s19 {
|
||||
font-size: 2.6em;
|
||||
}
|
||||
|
||||
.s18 {
|
||||
font-size: 2.5em;
|
||||
}
|
||||
|
||||
.s17 {
|
||||
font-size: 2.4em;
|
||||
}
|
||||
|
||||
.s16 {
|
||||
font-size: 2.3em;
|
||||
}
|
||||
|
||||
.s15 {
|
||||
font-size: 2.2em;
|
||||
}
|
||||
|
||||
.s14 {
|
||||
font-size: 2.1em;
|
||||
}
|
||||
|
||||
.s13 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.s12 {
|
||||
font-size: 1.9em;
|
||||
}
|
||||
|
||||
.s11 {
|
||||
font-size: 1.8em;
|
||||
}
|
||||
|
||||
.s10 {
|
||||
font-size: 1.7em;
|
||||
}
|
||||
|
||||
.s9 {
|
||||
font-size: 1.6em;
|
||||
}
|
||||
|
||||
.s8 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.s7 {
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
||||
.s6 {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.s5 {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.s4 {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.s3 {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.s2 {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.s1 {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.s0 {
|
||||
font-size: 0.7em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -55,7 +55,7 @@ export class AddonBlockTimelineHandler extends CoreBlockBaseHandler {
|
|||
* @param {number} instanceId The instance ID associated with the context level.
|
||||
* @return {CoreBlockHandlerData|Promise<CoreBlockHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData?(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
|
||||
: CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
|
||||
|
||||
return {
|
||||
|
|
|
@ -17,12 +17,14 @@ import { CoreMainMenuDelegate } from '@core/mainmenu/providers/delegate';
|
|||
import { CoreUserDelegate } from '@core/user/providers/user-delegate';
|
||||
import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate';
|
||||
import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate';
|
||||
import { CoreTagAreaDelegate } from '@core/tag/providers/area-delegate';
|
||||
import { AddonBlogProvider } from './providers/blog';
|
||||
import { AddonBlogMainMenuHandler } from './providers/mainmenu-handler';
|
||||
import { AddonBlogUserHandler } from './providers/user-handler';
|
||||
import { AddonBlogCourseOptionHandler } from './providers/course-option-handler';
|
||||
import { AddonBlogComponentsModule } from './components/components.module';
|
||||
import { AddonBlogIndexLinkHandler } from './providers/index-link-handler';
|
||||
import { AddonBlogTagAreaHandler } from './providers/tag-area-handler';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
@ -35,17 +37,20 @@ import { AddonBlogIndexLinkHandler } from './providers/index-link-handler';
|
|||
AddonBlogMainMenuHandler,
|
||||
AddonBlogUserHandler,
|
||||
AddonBlogCourseOptionHandler,
|
||||
AddonBlogIndexLinkHandler
|
||||
AddonBlogIndexLinkHandler,
|
||||
AddonBlogTagAreaHandler
|
||||
]
|
||||
})
|
||||
export class AddonBlogModule {
|
||||
constructor(mainMenuDelegate: CoreMainMenuDelegate, menuHandler: AddonBlogMainMenuHandler,
|
||||
userHandler: AddonBlogUserHandler, userDelegate: CoreUserDelegate,
|
||||
courseOptionHandler: AddonBlogCourseOptionHandler, courseOptionsDelegate: CoreCourseOptionsDelegate,
|
||||
linkHandler: AddonBlogIndexLinkHandler, contentLinksDelegate: CoreContentLinksDelegate) {
|
||||
linkHandler: AddonBlogIndexLinkHandler, contentLinksDelegate: CoreContentLinksDelegate,
|
||||
tagAreaDelegate: CoreTagAreaDelegate, tagAreaHandler: AddonBlogTagAreaHandler) {
|
||||
mainMenuDelegate.registerHandler(menuHandler);
|
||||
userDelegate.registerHandler(userHandler);
|
||||
courseOptionsDelegate.registerHandler(courseOptionHandler);
|
||||
contentLinksDelegate.registerHandler(linkHandler);
|
||||
tagAreaDelegate.registerHandler(tagAreaHandler);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import { CoreComponentsModule } from '@components/components.module';
|
|||
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||
import { CorePipesModule } from '@pipes/pipes.module';
|
||||
import { CoreCommentsComponentsModule } from '@core/comments/components/components.module';
|
||||
import { CoreTagComponentsModule } from '@core/tag/components/components.module';
|
||||
import { AddonBlogEntriesComponent } from './entries/entries';
|
||||
|
||||
@NgModule({
|
||||
|
@ -33,7 +34,8 @@ import { AddonBlogEntriesComponent } from './entries/entries';
|
|||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
CorePipesModule,
|
||||
CoreCommentsComponentsModule
|
||||
CoreCommentsComponentsModule,
|
||||
CoreTagComponentsModule
|
||||
],
|
||||
providers: [
|
||||
],
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
</ion-refresher>
|
||||
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
||||
<div class="safe-padding-horizontal">
|
||||
<ion-item *ngIf="showMyIssuesToggle">
|
||||
<ion-item *ngIf="showMyEntriesToggle">
|
||||
<ion-label>{{ 'addon.blog.showonlyyourentries' | translate }}</ion-label>
|
||||
<ion-toggle [(ngModel)]="onlyMyEntries"></ion-toggle>
|
||||
<ion-toggle [(ngModel)]="onlyMyEntries" (ionChange)="onlyMyEntriesToggleChanged(onlyMyEntries)">></ion-toggle>
|
||||
</ion-item>
|
||||
</div>
|
||||
<core-empty-box *ngIf="entries && entries.length == 0" icon="fa-newspaper-o" [message]="'addon.blog.noentriesyet' | translate"></core-empty-box>
|
||||
|
@ -29,6 +29,10 @@
|
|||
</ion-item>
|
||||
<ion-card-content>
|
||||
<core-format-text [text]="entry.summary" [component]="this.component" [componentId]="entry.id"></core-format-text>
|
||||
<ion-item text-wrap *ngIf="tagsEnabled && entry.tags && entry.tags.length > 0">
|
||||
<div item-start>{{ 'core.tag.tags' | translate }}:</div>
|
||||
<core-tag-list [tags]="entry.tags"></core-tag-list>
|
||||
</ion-item>
|
||||
<ion-item *ngIf="commentsEnabled">
|
||||
<core-comments [component]="this.component" [itemId]="entry.id" area="format_blog" [instanceId]="entry.userid" contextLevel="user"></core-comments>
|
||||
</ion-item>
|
||||
|
|
|
@ -15,10 +15,12 @@
|
|||
import { Component, Input, OnInit, ViewChild } from '@angular/core';
|
||||
import { Content } from 'ionic-angular';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreUserProvider } from '@core/user/providers/user';
|
||||
import { AddonBlogProvider } from '../../providers/blog';
|
||||
import { CoreCommentsProvider } from '@core/comments/providers/comments';
|
||||
import { CoreTagProvider } from '@core/tag/providers/tag';
|
||||
|
||||
/**
|
||||
* Component that displays the blog entries.
|
||||
|
@ -37,6 +39,9 @@ export class AddonBlogEntriesComponent implements OnInit {
|
|||
|
||||
protected filter = {};
|
||||
protected pageLoaded = 0;
|
||||
protected userPageLoaded = 0;
|
||||
protected canLoadMoreEntries = false;
|
||||
protected canLoadMoreUserEntries = true;
|
||||
|
||||
@ViewChild(Content) content: Content;
|
||||
|
||||
|
@ -45,14 +50,15 @@ export class AddonBlogEntriesComponent implements OnInit {
|
|||
loadMoreError = false;
|
||||
entries = [];
|
||||
currentUserId: number;
|
||||
showMyIssuesToggle = false;
|
||||
showMyEntriesToggle = false;
|
||||
onlyMyEntries = false;
|
||||
component = AddonBlogProvider.COMPONENT;
|
||||
commentsEnabled: boolean;
|
||||
tagsEnabled: boolean;
|
||||
|
||||
constructor(protected blogProvider: AddonBlogProvider, protected domUtils: CoreDomUtilsProvider,
|
||||
protected userProvider: CoreUserProvider, sitesProvider: CoreSitesProvider,
|
||||
protected commentsProvider: CoreCommentsProvider) {
|
||||
protected userProvider: CoreUserProvider, sitesProvider: CoreSitesProvider, protected utils: CoreUtilsProvider,
|
||||
protected commentsProvider: CoreCommentsProvider, private tagProvider: CoreTagProvider) {
|
||||
this.currentUserId = sitesProvider.getCurrentSiteUserId();
|
||||
}
|
||||
|
||||
|
@ -63,6 +69,7 @@ export class AddonBlogEntriesComponent implements OnInit {
|
|||
if (this.userId) {
|
||||
this.filter['userid'] = this.userId;
|
||||
}
|
||||
this.showMyEntriesToggle = !this.userId;
|
||||
|
||||
if (this.courseId) {
|
||||
this.filter['courseid'] = this.courseId;
|
||||
|
@ -85,6 +92,7 @@ export class AddonBlogEntriesComponent implements OnInit {
|
|||
}
|
||||
|
||||
this.commentsEnabled = !this.commentsProvider.areCommentsDisabledInSite();
|
||||
this.tagsEnabled = this.tagProvider.areTagsAvailableInSite();
|
||||
|
||||
this.fetchEntries().then(() => {
|
||||
this.blogProvider.logView(this.filter).catch(() => {
|
||||
|
@ -104,9 +112,12 @@ export class AddonBlogEntriesComponent implements OnInit {
|
|||
|
||||
if (refresh) {
|
||||
this.pageLoaded = 0;
|
||||
this.userPageLoaded = 0;
|
||||
}
|
||||
|
||||
return this.blogProvider.getEntries(this.filter, this.pageLoaded).then((result) => {
|
||||
const loadPage = this.onlyMyEntries ? this.userPageLoaded : this.pageLoaded;
|
||||
|
||||
return this.blogProvider.getEntries(this.filter, loadPage).then((result) => {
|
||||
const promises = result.entries.map((entry) => {
|
||||
switch (entry.publishstate) {
|
||||
case 'draft':
|
||||
|
@ -131,16 +142,25 @@ export class AddonBlogEntriesComponent implements OnInit {
|
|||
});
|
||||
|
||||
if (refresh) {
|
||||
this.showMyIssuesToggle = false;
|
||||
this.entries = result.entries;
|
||||
} else {
|
||||
this.entries = this.entries.concat(result.entries);
|
||||
this.entries = this.utils.uniqueArray(this.entries.concat(result.entries), 'id').sort((a, b) => {
|
||||
return b.created - a.created;
|
||||
});
|
||||
}
|
||||
|
||||
this.canLoadMore = result.totalentries > this.entries.length;
|
||||
this.pageLoaded++;
|
||||
|
||||
this.showMyIssuesToggle = !this.userId;
|
||||
if (this.onlyMyEntries) {
|
||||
const count = this.entries.filter((entry) => {
|
||||
return entry.userid == this.currentUserId;
|
||||
}).length;
|
||||
this.canLoadMoreUserEntries = result.totalentries > count;
|
||||
this.canLoadMore = this.canLoadMoreUserEntries;
|
||||
this.userPageLoaded++;
|
||||
} else {
|
||||
this.canLoadMoreEntries = result.totalentries > this.entries.length;
|
||||
this.canLoadMore = this.canLoadMoreEntries;
|
||||
this.pageLoaded++;
|
||||
}
|
||||
|
||||
return Promise.all(promises);
|
||||
}).catch((message) => {
|
||||
|
@ -151,6 +171,30 @@ export class AddonBlogEntriesComponent implements OnInit {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle between showing only my entries or not.
|
||||
*
|
||||
* @param {boolean} enabled If true, filter my entries. False otherwise.
|
||||
*/
|
||||
onlyMyEntriesToggleChanged(enabled: boolean): void {
|
||||
if (enabled) {
|
||||
const count = this.entries.filter((entry) => {
|
||||
return entry.userid == this.currentUserId;
|
||||
}).length;
|
||||
this.userPageLoaded = Math.floor(count / AddonBlogProvider.ENTRIES_PER_PAGE);
|
||||
this.filter['userid'] = this.currentUserId;
|
||||
|
||||
if (count == 0 && this.canLoadMoreUserEntries) {
|
||||
// First time but no entry loaded. Try to load some.
|
||||
this.loadMore();
|
||||
}
|
||||
} else {
|
||||
delete this.filter['userid'];
|
||||
}
|
||||
|
||||
this.canLoadMore = enabled ? this.canLoadMoreUserEntries : this.canLoadMoreEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to load more entries.
|
||||
*
|
||||
|
@ -169,7 +213,23 @@ export class AddonBlogEntriesComponent implements OnInit {
|
|||
* @param {any} refresher Refresher instance.
|
||||
*/
|
||||
refresh(refresher?: any): void {
|
||||
this.blogProvider.invalidateEntries(this.filter).finally(() => {
|
||||
const promises = this.entries.map((entry) => {
|
||||
return this.commentsProvider.invalidateCommentsData('user', entry.userid, this.component, entry.id, 'format_blog');
|
||||
});
|
||||
|
||||
promises.push(this.blogProvider.invalidateEntries(this.filter));
|
||||
|
||||
if (this.showMyEntriesToggle) {
|
||||
this.filter['userid'] = this.currentUserId;
|
||||
promises.push(this.blogProvider.invalidateEntries(this.filter));
|
||||
|
||||
if (!this.onlyMyEntries) {
|
||||
delete this.filter['userid'];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.utils.allPromises(promises).finally(() => {
|
||||
this.fetchEntries(true).finally(() => {
|
||||
if (refresher) {
|
||||
refresher.complete();
|
||||
|
|
|
@ -78,10 +78,10 @@ export class AddonBlogCourseOptionHandler implements CoreCourseOptionsHandler {
|
|||
* Returns the data needed to render the handler.
|
||||
*
|
||||
* @param {Injector} injector Injector.
|
||||
* @param {number} courseId The course ID.
|
||||
* @param {number} course The course.
|
||||
* @return {CoreCourseOptionsHandlerData|Promise<CoreCourseOptionsHandlerData>} Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData(injector: Injector, courseId: number): CoreCourseOptionsHandlerData | Promise<CoreCourseOptionsHandlerData> {
|
||||
getDisplayData(injector: Injector, course: any): CoreCourseOptionsHandlerData | Promise<CoreCourseOptionsHandlerData> {
|
||||
return {
|
||||
title: 'addon.blog.blog',
|
||||
class: 'addon-blog-handler',
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
|
||||
import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
|
||||
import { CoreLoginHelperProvider } from '@core/login/providers/helper';
|
||||
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
|
||||
import { AddonBlogProvider } from './blog';
|
||||
|
||||
/**
|
||||
|
@ -27,7 +27,7 @@ export class AddonBlogIndexLinkHandler extends CoreContentLinksHandlerBase {
|
|||
featureName = 'CoreUserDelegate_AddonBlog:blogs';
|
||||
pattern = /\/blog\/index\.php/;
|
||||
|
||||
constructor(private blogProvider: AddonBlogProvider, private loginHelper: CoreLoginHelperProvider) {
|
||||
constructor(private blogProvider: AddonBlogProvider, private linkHelper: CoreContentLinksHelperProvider) {
|
||||
super();
|
||||
}
|
||||
|
||||
|
@ -53,8 +53,7 @@ export class AddonBlogIndexLinkHandler extends CoreContentLinksHandlerBase {
|
|||
|
||||
return [{
|
||||
action: (siteId, navCtrl?): void => {
|
||||
// Always use redirect to make it the new history root (to avoid "loops" in history).
|
||||
this.loginHelper.redirect('AddonBlogEntriesPage', pageParams, siteId);
|
||||
this.linkHelper.goInSite(navCtrl, 'AddonBlogEntriesPage', pageParams, siteId, !Object.keys(pageParams).length);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
// (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 { CoreTagAreaHandler } from '@core/tag/providers/area-delegate';
|
||||
import { CoreTagHelperProvider } from '@core/tag/providers/helper';
|
||||
import { CoreTagFeedComponent } from '@core/tag/components/feed/feed';
|
||||
import { AddonBlogProvider } from './blog';
|
||||
|
||||
/**
|
||||
* Handler to support tags.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonBlogTagAreaHandler implements CoreTagAreaHandler {
|
||||
name = 'AddonBlogTagAreaHandler';
|
||||
type = 'core/post';
|
||||
|
||||
constructor(private tagHelper: CoreTagHelperProvider, private blogProvider: AddonBlogProvider) {}
|
||||
|
||||
/**
|
||||
* Whether or not the handler is enabled on a site level.
|
||||
* @return {boolean|Promise<boolean>} Whether or not the handler is enabled on a site level.
|
||||
*/
|
||||
isEnabled(): boolean | Promise<boolean> {
|
||||
return this.blogProvider.isPluginEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the rendered content of a tag index and returns the items.
|
||||
*
|
||||
* @param {string} content Rendered content.
|
||||
* @return {any[]|Promise<any[]>} Area items (or promise resolved with the items).
|
||||
*/
|
||||
parseContent(content: string): any[] | Promise<any[]> {
|
||||
return this.tagHelper.parseFeedContent(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the component to use to display items.
|
||||
*
|
||||
* @param {Injector} injector Injector.
|
||||
* @return {any|Promise<any>} The component (or promise resolved with component) to use, undefined if not found.
|
||||
*/
|
||||
getComponent(injector: Injector): any | Promise<any> {
|
||||
return CoreTagFeedComponent;
|
||||
}
|
||||
}
|
|
@ -63,7 +63,7 @@ export class AddonBlogUserHandler implements CoreUserProfileHandler {
|
|||
action: (event, navCtrl, user, courseId): void => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
// Always use redirect to make it the new history root (to avoid "loops" in history).
|
||||
|
||||
this.linkHelper.goInSite(navCtrl, 'AddonBlogEntriesPage', { userId: user.id, courseId: courseId });
|
||||
}
|
||||
};
|
||||
|
|
|
@ -14,18 +14,26 @@
|
|||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { AddonCalendarProvider } from './providers/calendar';
|
||||
import { AddonCalendarOfflineProvider } from './providers/calendar-offline';
|
||||
import { AddonCalendarHelperProvider } from './providers/helper';
|
||||
import { AddonCalendarSyncProvider } from './providers/calendar-sync';
|
||||
import { AddonCalendarMainMenuHandler } from './providers/mainmenu-handler';
|
||||
import { AddonCalendarSyncCronHandler } from './providers/sync-cron-handler';
|
||||
import { AddonCalendarViewLinkHandler } from './providers/view-link-handler';
|
||||
import { CoreMainMenuDelegate } from '@core/mainmenu/providers/delegate';
|
||||
import { CoreCronDelegate } from '@providers/cron';
|
||||
import { CoreInitDelegate } from '@providers/init';
|
||||
import { CoreLocalNotificationsProvider } from '@providers/local-notifications';
|
||||
import { CoreLoginHelperProvider } from '@core/login/providers/helper';
|
||||
import { CoreUpdateManagerProvider } from '@providers/update-manager';
|
||||
import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate';
|
||||
|
||||
// List of providers (without handlers).
|
||||
export const ADDON_CALENDAR_PROVIDERS: any[] = [
|
||||
AddonCalendarProvider,
|
||||
AddonCalendarHelperProvider
|
||||
AddonCalendarOfflineProvider,
|
||||
AddonCalendarHelperProvider,
|
||||
AddonCalendarSyncProvider
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@ -35,15 +43,24 @@ export const ADDON_CALENDAR_PROVIDERS: any[] = [
|
|||
],
|
||||
providers: [
|
||||
AddonCalendarProvider,
|
||||
AddonCalendarOfflineProvider,
|
||||
AddonCalendarHelperProvider,
|
||||
AddonCalendarMainMenuHandler
|
||||
AddonCalendarSyncProvider,
|
||||
AddonCalendarMainMenuHandler,
|
||||
AddonCalendarSyncCronHandler,
|
||||
AddonCalendarViewLinkHandler
|
||||
]
|
||||
})
|
||||
export class AddonCalendarModule {
|
||||
constructor(mainMenuDelegate: CoreMainMenuDelegate, calendarHandler: AddonCalendarMainMenuHandler,
|
||||
initDelegate: CoreInitDelegate, calendarProvider: AddonCalendarProvider, loginHelper: CoreLoginHelperProvider,
|
||||
localNotificationsProvider: CoreLocalNotificationsProvider, updateManager: CoreUpdateManagerProvider) {
|
||||
localNotificationsProvider: CoreLocalNotificationsProvider, updateManager: CoreUpdateManagerProvider,
|
||||
cronDelegate: CoreCronDelegate, syncHandler: AddonCalendarSyncCronHandler,
|
||||
contentLinksDelegate: CoreContentLinksDelegate, viewLinkHandler: AddonCalendarViewLinkHandler) {
|
||||
|
||||
mainMenuDelegate.registerHandler(calendarHandler);
|
||||
cronDelegate.register(syncHandler);
|
||||
contentLinksDelegate.registerHandler(viewLinkHandler);
|
||||
|
||||
initDelegate.ready().then(() => {
|
||||
calendarProvider.scheduleAllSitesEventsNotifications();
|
||||
|
@ -58,7 +75,13 @@ export class AddonCalendarModule {
|
|||
return;
|
||||
}
|
||||
|
||||
loginHelper.redirect('AddonCalendarListPage', {eventId: data.eventid}, data.siteId);
|
||||
// Check which page we should load.
|
||||
calendarProvider.canViewMonth(data.siteId).then((canView) => {
|
||||
const pageName = canView ? 'AddonCalendarIndexPage' : 'AddonCalendarListPage';
|
||||
|
||||
loginHelper.redirect(pageName, {eventId: data.eventid}, data.siteId);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
|
||||
<!-- Add buttons to the nav bar. -->
|
||||
<core-navbar-buttons end prepend>
|
||||
<core-context-menu>
|
||||
<core-context-menu-item *ngIf="canNavigate && !isCurrentMonth && displayNavButtons" [priority]="900" [content]="'addon.calendar.currentmonth' | translate" [iconAction]="'fa-calendar-times-o'" (action)="goToCurrentMonth()"></core-context-menu-item>
|
||||
</core-context-menu>
|
||||
</core-navbar-buttons>
|
||||
|
||||
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
||||
<!-- Period name and arrows to navigate. -->
|
||||
<ion-grid no-padding class="addon-calendar-navigation">
|
||||
<ion-row align-items-center>
|
||||
<ion-col text-start *ngIf="canNavigate">
|
||||
<a ion-button icon-only clear (click)="loadPrevious()" [title]="'core.previous' | translate">
|
||||
<ion-icon name="arrow-back" md="ios-arrow-back"></ion-icon>
|
||||
</a>
|
||||
</ion-col>
|
||||
<ion-col text-center class="addon-calendar-period">
|
||||
<h3>{{ periodName }}</h3>
|
||||
</ion-col>
|
||||
<ion-col text-end *ngIf="canNavigate">
|
||||
<a ion-button icon-only clear (click)="loadNext()" [title]="'core.next' | translate">
|
||||
<ion-icon name="arrow-forward" md="ios-arrow-forward"></ion-icon>
|
||||
</a>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
|
||||
<!-- Calendar view. -->
|
||||
<ion-grid class="addon-calendar-months">
|
||||
<!-- List of days. -->
|
||||
<ion-row>
|
||||
<ion-col text-center *ngFor="let day of weekDays" class="addon-calendar-weekday">
|
||||
<span class="hidden-tablet" [title]="day.fullname | translate">{{ day.shortname | translate }}</span>
|
||||
<span class="hidden-phone">{{ day.fullname | translate }}</span>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
|
||||
<!-- Weeks. -->
|
||||
<ion-row *ngFor="let week of weeks" class="addon-calendar-week">
|
||||
<ion-col *ngFor="let value of week.prepadding" class="dayblank addon-calendar-day"></ion-col> <!-- Empty slots (first week). -->
|
||||
<ion-col text-center *ngFor="let day of week.days" (click)="dayClicked(day.mday)" [ngClass]='{"hasevents": day.hasevents, "today": isCurrentMonth && day.istoday, "weekend": day.isweekend, "duration_finish": day.haslastdayofevent}' class="addon-calendar-day" [class.addon-calendar-event-past-day]="isPastMonth || day.ispast">
|
||||
<p class="addon-calendar-day-number"><span>{{ day.mday }}</span></p>
|
||||
|
||||
<!-- In phone, display some dots to indicate the type of events. -->
|
||||
<p class="hidden-tablet addon-calendar-dot-types"><span *ngFor="let type of day.calendareventtypes" class="calendar_event_type calendar_event_{{type}}"></span></p>
|
||||
|
||||
<!-- In tablet, display list of events. -->
|
||||
<div class="hidden-phone addon-calendar-day-events">
|
||||
<ng-container *ngFor="let event of day.filteredEvents | slice:0:4; let index = index">
|
||||
<p *ngIf="index < 3 || day.filteredEvents.length == 4" class="addon-calendar-event" (click)="eventClicked(event, $event)" [class.addon-calendar-event-past]="event.ispast">
|
||||
<span class="calendar_event_type calendar_event_{{event.formattedType}}"></span>
|
||||
<ion-icon *ngIf="event.offline && !event.deleted" name="time"></ion-icon>
|
||||
<ion-icon *ngIf="event.deleted" name="trash"></ion-icon>
|
||||
<span class="addon-calendar-event-time">{{ event.timestart * 1000 | coreFormatDate: timeFormat }}</span>
|
||||
<img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" alt="" role="presentation" class="core-module-icon">
|
||||
<span class="addon-calendar-event-name">{{event.name}}</span>
|
||||
</p>
|
||||
</ng-container>
|
||||
<p *ngIf="day.filteredEvents.length > 4" class="addon-calendar-day-more"><b>{{ 'core.nummore' | translate:{$a: day.filteredEvents.length - 3} }}</b></p>
|
||||
</div>
|
||||
</ion-col>
|
||||
<ion-col *ngFor="let value of week.postpadding" class="dayblank addon-calendar-day"></ion-col> <!-- Empty slots (last week). -->
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
|
||||
</core-loading>
|
|
@ -0,0 +1,196 @@
|
|||
|
||||
$calendar-event-category-color: $purple !default; // Purple.
|
||||
$calendar-event-course-color: $red !default; // Red.
|
||||
$calendar-event-group-color: $yellow !default; // Yellow.
|
||||
$calendar-event-user-color: $blue !default; // Blue.
|
||||
$calendar-event-site-color: $green !default; // Green.
|
||||
$calendar-today-bgcolor: $core-color !default;
|
||||
$calendar-today-color: $white !default;
|
||||
$calendar-border-color: $gray !default;
|
||||
|
||||
ion-app.app-root page-addon-calendar-list,
|
||||
ion-app.app-root page-addon-calendar-day,
|
||||
ion-app.app-root addon-calendar-upcoming-events {
|
||||
|
||||
.item.addon-calendar-event {
|
||||
> .icon {
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
|
||||
&.fa {
|
||||
font-size: 20px;
|
||||
&::before {
|
||||
width: 1.9em;
|
||||
}
|
||||
}
|
||||
}
|
||||
> .core-module-icon {
|
||||
margin: 9px 8px 9px 8px;
|
||||
}
|
||||
|
||||
&.addon-calendar-eventtype-category > .icon {
|
||||
background-color: $calendar-event-category-color;
|
||||
}
|
||||
&.addon-calendar-eventtype-course > .icon {
|
||||
background-color: $calendar-event-course-color;
|
||||
}
|
||||
&.addon-calendar-eventtype-group > .icon {
|
||||
background-color: $calendar-event-group-color;
|
||||
}
|
||||
&.addon-calendar-eventtype-user > .icon {
|
||||
background-color: $calendar-event-user-color;
|
||||
}
|
||||
&.addon-calendar-eventtype-site > .icon {
|
||||
background-color: $calendar-event-site-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ion-app.app-root addon-calendar-calendar {
|
||||
|
||||
.addon-calendar-navigation {
|
||||
@include padding(5px, 10px, null, 10px);
|
||||
}
|
||||
|
||||
.addon-calendar-months {
|
||||
background-color: white;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.addon-calendar-day {
|
||||
border-bottom: 1px solid $calendar-border-color;
|
||||
@include border-end(1px, solid, $calendar-border-color);
|
||||
overflow: hidden;
|
||||
min-height: 60px;
|
||||
|
||||
&:first-child {
|
||||
@include padding(null, null, null, 10px);
|
||||
}
|
||||
&:last-child {
|
||||
@include border-end(0, null, null);
|
||||
@include padding(null, 8px, null, null);
|
||||
}
|
||||
|
||||
&.addon-calendar-event-past-day > .addon-calendar-dot-types,
|
||||
&.addon-calendar-event-past-day > .addon-calendar-day-events {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.addon-calendar-day-number {
|
||||
margin: 0;
|
||||
|
||||
span {
|
||||
line-height: 24px;
|
||||
font-weight: 500;
|
||||
display: inline-block;
|
||||
margin: 3px;
|
||||
width: max-content;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
.addon-calendar-day-number {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
&.today .addon-calendar-day-number span {
|
||||
background-color: $calendar-today-bgcolor;
|
||||
color: $calendar-today-color;
|
||||
|
||||
border-radius: 50%;
|
||||
}
|
||||
&.dayblank {
|
||||
background-color: $gray-lighter;
|
||||
}
|
||||
|
||||
.addon-calendar-event {
|
||||
margin-top: 0.6em;
|
||||
margin-bottom: 0.6em;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
&.addon-calendar-event-past {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.addon-calendar-event-name {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.addon-calendar-day-more {
|
||||
@include margin(0.6em, null, 0.6em, 4px);
|
||||
}
|
||||
|
||||
.addon-calendar-dot-types {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.addon-calendar-period {
|
||||
flex-grow: 3;
|
||||
h3 {
|
||||
margin-top: 10px;
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
}
|
||||
|
||||
.addon-calendar-weekday {
|
||||
border-bottom: 1px solid $list-md-border-color;
|
||||
}
|
||||
|
||||
.addon-calendar-day-events {
|
||||
@include text-align('start');
|
||||
|
||||
ion-icon {
|
||||
@include margin-horizontal(null, 2px);
|
||||
font-size: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.addon-calendar-event, .addon-calendar-day-number, .addon-calendar-day-more {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.calendar_event_type {
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid white;
|
||||
@include margin-horizontal(1px, 1px);
|
||||
|
||||
|
||||
|
||||
&.calendar_event_category {
|
||||
background-color: $calendar-event-category-color;
|
||||
}
|
||||
&.calendar_event_course {
|
||||
background-color: $calendar-event-course-color;
|
||||
}
|
||||
&.calendar_event_group {
|
||||
background-color: $calendar-event-group-color;
|
||||
}
|
||||
&.calendar_event_user {
|
||||
background-color: $calendar-event-user-color;
|
||||
}
|
||||
&.calendar_event_site {
|
||||
background-color: $calendar-event-site-color;
|
||||
}
|
||||
}
|
||||
|
||||
.core-module-icon {
|
||||
@include margin-horizontal(1px, 1px);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,515 @@
|
|||
// (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, OnDestroy, OnInit, Input, OnChanges, SimpleChange, Output, EventEmitter } from '@angular/core';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreLocalNotificationsProvider } from '@providers/local-notifications';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { AddonCalendarProvider } from '../../providers/calendar';
|
||||
import { AddonCalendarHelperProvider } from '../../providers/helper';
|
||||
import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline';
|
||||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
||||
import { CoreAppProvider } from '@providers/app';
|
||||
|
||||
/**
|
||||
* Component that displays a calendar.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'addon-calendar-calendar',
|
||||
templateUrl: 'addon-calendar-calendar.html',
|
||||
})
|
||||
export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDestroy {
|
||||
@Input() initialYear: number | string; // Initial year to load.
|
||||
@Input() initialMonth: number | string; // Initial month to load.
|
||||
@Input() courseId: number | string;
|
||||
@Input() categoryId: number | string; // Category ID the course belongs to.
|
||||
@Input() canNavigate?: string | boolean; // Whether to include arrows to change the month. Defaults to true.
|
||||
@Input() displayNavButtons?: string | boolean; // Whether to display nav buttons created by this component. Defaults to true.
|
||||
@Output() onEventClicked = new EventEmitter<number>();
|
||||
@Output() onDayClicked = new EventEmitter<{day: number, month: number, year: number}>();
|
||||
|
||||
periodName: string;
|
||||
weekDays: any[];
|
||||
weeks: any[];
|
||||
loaded = false;
|
||||
timeFormat: string;
|
||||
isCurrentMonth: boolean;
|
||||
isPastMonth: boolean;
|
||||
|
||||
protected year: number;
|
||||
protected month: number;
|
||||
protected categoriesRetrieved = false;
|
||||
protected categories = {};
|
||||
protected currentSiteId: string;
|
||||
protected offlineEvents: {[monthId: string]: {[day: number]: any[]}} = {}; // Offline events classified in month & day.
|
||||
protected offlineEditedEventsIds = []; // IDs of events edited in offline.
|
||||
protected deletedEvents = []; // Events deleted in offline.
|
||||
protected currentTime: number;
|
||||
|
||||
// Observers.
|
||||
protected undeleteEventObserver: any;
|
||||
protected obsDefaultTimeChange: any;
|
||||
|
||||
constructor(eventsProvider: CoreEventsProvider,
|
||||
sitesProvider: CoreSitesProvider,
|
||||
localNotificationsProvider: CoreLocalNotificationsProvider,
|
||||
private calendarProvider: AddonCalendarProvider,
|
||||
private calendarHelper: AddonCalendarHelperProvider,
|
||||
private calendarOffline: AddonCalendarOfflineProvider,
|
||||
private domUtils: CoreDomUtilsProvider,
|
||||
private timeUtils: CoreTimeUtilsProvider,
|
||||
private utils: CoreUtilsProvider,
|
||||
private coursesProvider: CoreCoursesProvider,
|
||||
private appProvider: CoreAppProvider) {
|
||||
|
||||
this.currentSiteId = sitesProvider.getCurrentSiteId();
|
||||
|
||||
if (localNotificationsProvider.isAvailable()) {
|
||||
// Re-schedule events if default time changes.
|
||||
this.obsDefaultTimeChange = eventsProvider.on(AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_CHANGED, () => {
|
||||
this.weeks.forEach((week) => {
|
||||
week.days.forEach((day) => {
|
||||
calendarProvider.scheduleEventsNotifications(day.events);
|
||||
});
|
||||
});
|
||||
}, this.currentSiteId);
|
||||
}
|
||||
|
||||
// Listen for events "undeleted" (offline).
|
||||
this.undeleteEventObserver = eventsProvider.on(AddonCalendarProvider.UNDELETED_EVENT_EVENT, (data) => {
|
||||
if (data && data.eventId) {
|
||||
// Mark it as undeleted, no need to refresh.
|
||||
this.undeleteEvent(data.eventId);
|
||||
|
||||
// Remove it from the list of deleted events if it's there.
|
||||
const index = this.deletedEvents.indexOf(data.eventId);
|
||||
if (index != -1) {
|
||||
this.deletedEvents.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}, this.currentSiteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component loaded.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
const now = new Date();
|
||||
|
||||
this.year = this.initialYear ? Number(this.initialYear) : now.getFullYear();
|
||||
this.month = this.initialMonth ? Number(this.initialMonth) : now.getMonth() + 1;
|
||||
|
||||
this.calculateIsCurrentMonth();
|
||||
|
||||
this.fetchData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect changes on input properties.
|
||||
*/
|
||||
ngOnChanges(changes: {[name: string]: SimpleChange}): void {
|
||||
this.canNavigate = typeof this.canNavigate == 'undefined' ? true : this.utils.isTrueOrOne(this.canNavigate);
|
||||
this.displayNavButtons = typeof this.displayNavButtons == 'undefined' ? true :
|
||||
this.utils.isTrueOrOne(this.displayNavButtons);
|
||||
|
||||
if ((changes.courseId || changes.categoryId) && this.weeks) {
|
||||
this.filterEvents();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch contacts.
|
||||
*
|
||||
* @param {boolean} [refresh=false] True if we are refreshing events.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
fetchData(refresh: boolean = false): Promise<any> {
|
||||
const promises = [];
|
||||
|
||||
promises.push(this.loadCategories());
|
||||
|
||||
// Get offline events.
|
||||
promises.push(this.calendarOffline.getAllEditedEvents().then((events) => {
|
||||
// Format data.
|
||||
events.forEach((event) => {
|
||||
event.offline = true;
|
||||
this.calendarHelper.formatEventData(event);
|
||||
});
|
||||
|
||||
// Classify them by month.
|
||||
this.offlineEvents = this.calendarHelper.classifyIntoMonths(events);
|
||||
|
||||
// Get the IDs of events edited in offline.
|
||||
const filtered = events.filter((event) => {
|
||||
return event.id > 0;
|
||||
});
|
||||
this.offlineEditedEventsIds = filtered.map((event) => {
|
||||
return event.id;
|
||||
});
|
||||
}));
|
||||
|
||||
// Get events deleted in offline.
|
||||
promises.push(this.calendarOffline.getAllDeletedEventsIds().then((ids) => {
|
||||
this.deletedEvents = ids;
|
||||
}));
|
||||
|
||||
// Get time format to use.
|
||||
promises.push(this.calendarProvider.getCalendarTimeFormat().then((value) => {
|
||||
this.timeFormat = value;
|
||||
}));
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
return this.fetchEvents();
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true);
|
||||
}).finally(() => {
|
||||
this.loaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the events for current month.
|
||||
*
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
fetchEvents(): Promise<any> {
|
||||
// Don't pass courseId and categoryId, we'll filter them locally.
|
||||
return this.calendarProvider.getMonthlyEvents(this.year, this.month).catch((error) => {
|
||||
if (!this.appProvider.isOnline()) {
|
||||
// Allow navigating to non-cached months in offline (behave as if using emergency cache).
|
||||
return this.calendarHelper.getOfflineMonthWeeks(this.year, this.month);
|
||||
} else {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}).then((result) => {
|
||||
// Calculate the period name. We don't use the one in result because it's in server's language.
|
||||
this.periodName = this.timeUtils.userDate(new Date(this.year, this.month - 1).getTime(), 'core.strftimemonthyear');
|
||||
|
||||
this.weekDays = this.calendarProvider.getWeekDays(result.daynames[0].dayno);
|
||||
this.weeks = result.weeks;
|
||||
|
||||
this.calculateIsCurrentMonth();
|
||||
|
||||
if (this.isCurrentMonth) {
|
||||
const currentDay = new Date().getDate();
|
||||
let isPast = true;
|
||||
|
||||
this.weeks.forEach((week) => {
|
||||
week.days.some((day) => {
|
||||
day.istoday = day.mday == currentDay;
|
||||
day.ispast = isPast && !day.istoday;
|
||||
isPast = day.ispast;
|
||||
|
||||
if (day.istoday) {
|
||||
day.events.forEach((event) => {
|
||||
event.ispast = this.isEventPast(event);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return day.istoday;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Merge the online events with offline data.
|
||||
this.mergeEvents();
|
||||
|
||||
// Filter events by course.
|
||||
this.filterEvents();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load categories to be able to filter events.
|
||||
*
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
protected loadCategories(): Promise<any> {
|
||||
if (this.categoriesRetrieved) {
|
||||
// Already retrieved, stop.
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.coursesProvider.getCategories(0, true).then((cats) => {
|
||||
this.categoriesRetrieved = true;
|
||||
this.categories = {};
|
||||
|
||||
// Index categories by ID.
|
||||
cats.forEach((category) => {
|
||||
this.categories[category.id] = category;
|
||||
});
|
||||
}).catch(() => {
|
||||
// Ignore errors.
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter events to only display events belonging to a certain course.
|
||||
*/
|
||||
filterEvents(): void {
|
||||
const courseId = this.courseId ? Number(this.courseId) : undefined,
|
||||
categoryId = this.categoryId ? Number(this.categoryId) : undefined;
|
||||
|
||||
this.weeks.forEach((week) => {
|
||||
week.days.forEach((day) => {
|
||||
if (!courseId || courseId < 0) {
|
||||
day.filteredEvents = day.events;
|
||||
} else {
|
||||
day.filteredEvents = day.events.filter((event) => {
|
||||
return this.calendarHelper.shouldDisplayEvent(event, courseId, categoryId, this.categories);
|
||||
});
|
||||
}
|
||||
|
||||
// Re-calculate some properties.
|
||||
this.calendarHelper.calculateDayData(day, day.filteredEvents);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh events.
|
||||
*
|
||||
* @param {boolean} [afterChange] Whether the refresh is done after an event has changed or has been synced.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
refreshData(afterChange?: boolean): Promise<any> {
|
||||
const promises = [];
|
||||
|
||||
// Don't invalidate monthly events after a change, it has already been handled.
|
||||
if (!afterChange) {
|
||||
promises.push(this.calendarProvider.invalidateMonthlyEvents(this.year, this.month));
|
||||
}
|
||||
promises.push(this.coursesProvider.invalidateCategories(0, true));
|
||||
promises.push(this.calendarProvider.invalidateTimeFormat());
|
||||
|
||||
this.categoriesRetrieved = false; // Get categories again.
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
return this.fetchData(true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load next month.
|
||||
*/
|
||||
loadNext(): void {
|
||||
this.increaseMonth();
|
||||
|
||||
this.loaded = false;
|
||||
|
||||
this.fetchEvents().catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true);
|
||||
this.decreaseMonth();
|
||||
}).finally(() => {
|
||||
this.loaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load previous month.
|
||||
*/
|
||||
loadPrevious(): void {
|
||||
this.decreaseMonth();
|
||||
|
||||
this.loaded = false;
|
||||
|
||||
this.fetchEvents().catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true);
|
||||
this.increaseMonth();
|
||||
}).finally(() => {
|
||||
this.loaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* An event was clicked.
|
||||
*
|
||||
* @param {any} calendarEvent Calendar event..
|
||||
* @param {MouseEvent} event Mouse event.
|
||||
*/
|
||||
eventClicked(calendarEvent: any, event: MouseEvent): void {
|
||||
this.onEventClicked.emit(calendarEvent.id);
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
/**
|
||||
* A day was clicked.
|
||||
*
|
||||
* @param {number} day Day.
|
||||
*/
|
||||
dayClicked(day: number): void {
|
||||
this.onDayClicked.emit({day: day, month: this.month, year: this.year});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user is viewing the current month.
|
||||
*/
|
||||
calculateIsCurrentMonth(): void {
|
||||
const now = new Date();
|
||||
|
||||
this.currentTime = this.timeUtils.timestamp();
|
||||
|
||||
this.isCurrentMonth = this.year == now.getFullYear() && this.month == now.getMonth() + 1;
|
||||
this.isPastMonth = this.year < now.getFullYear() || (this.year == now.getFullYear() && this.month < now.getMonth() + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to current month.
|
||||
*/
|
||||
goToCurrentMonth(): void {
|
||||
const now = new Date(),
|
||||
initialMonth = this.month,
|
||||
initialYear = this.year;
|
||||
|
||||
this.month = now.getMonth() + 1;
|
||||
this.year = now.getFullYear();
|
||||
|
||||
this.loaded = false;
|
||||
|
||||
this.fetchEvents().then(() => {
|
||||
this.isCurrentMonth = true;
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true);
|
||||
this.year = initialYear;
|
||||
this.month = initialMonth;
|
||||
}).finally(() => {
|
||||
this.loaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrease the current month.
|
||||
*/
|
||||
protected decreaseMonth(): void {
|
||||
if (this.month === 1) {
|
||||
this.month = 12;
|
||||
this.year--;
|
||||
} else {
|
||||
this.month--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase the current month.
|
||||
*/
|
||||
protected increaseMonth(): void {
|
||||
if (this.month === 12) {
|
||||
this.month = 1;
|
||||
this.year++;
|
||||
} else {
|
||||
this.month++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge online events with the offline events of that period.
|
||||
*/
|
||||
protected mergeEvents(): void {
|
||||
const monthOfflineEvents = this.offlineEvents[this.calendarHelper.getMonthId(this.year, this.month)];
|
||||
|
||||
this.weeks.forEach((week) => {
|
||||
week.days.forEach((day) => {
|
||||
|
||||
// Format online events.
|
||||
day.events.forEach(this.calendarHelper.formatEventData.bind(this.calendarHelper));
|
||||
|
||||
// Schedule notifications for the events retrieved (only future events will be scheduled).
|
||||
this.calendarProvider.scheduleEventsNotifications(day.events);
|
||||
|
||||
if (monthOfflineEvents || this.deletedEvents.length) {
|
||||
// There is offline data, merge it.
|
||||
|
||||
if (this.deletedEvents.length) {
|
||||
// Mark as deleted the events that were deleted in offline.
|
||||
day.events.forEach((event) => {
|
||||
event.deleted = this.deletedEvents.indexOf(event.id) != -1;
|
||||
});
|
||||
}
|
||||
|
||||
if (this.offlineEditedEventsIds.length) {
|
||||
// Remove the online events that were modified in offline.
|
||||
day.events = day.events.filter((event) => {
|
||||
return this.offlineEditedEventsIds.indexOf(event.id) == -1;
|
||||
});
|
||||
}
|
||||
|
||||
if (monthOfflineEvents && monthOfflineEvents[day.mday]) {
|
||||
// Add the offline events (either new or edited).
|
||||
day.events = this.sortEvents(day.events.concat(monthOfflineEvents[day.mday]));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort events by timestart.
|
||||
*
|
||||
* @param {any[]} events List to sort.
|
||||
*/
|
||||
protected sortEvents(events: any[]): any[] {
|
||||
return events.sort((a, b) => {
|
||||
if (a.timestart == b.timestart) {
|
||||
return a.timeduration - b.timeduration;
|
||||
}
|
||||
|
||||
return a.timestart - b.timestart;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Undelete a certain event.
|
||||
*
|
||||
* @param {number} eventId Event ID.
|
||||
*/
|
||||
protected undeleteEvent(eventId: number): void {
|
||||
if (!this.weeks) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.weeks.forEach((week) => {
|
||||
week.days.forEach((day) => {
|
||||
const event = day.events.find((event) => {
|
||||
return event.id == eventId;
|
||||
});
|
||||
|
||||
if (event) {
|
||||
event.deleted = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the event is in the past or not.
|
||||
* @param {any} event Event object.
|
||||
* @return {boolean} True if it's in the past.
|
||||
*/
|
||||
isEventPast(event: any): boolean {
|
||||
return (event.timestart + event.timeduration) < this.currentTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.undeleteEventObserver && this.undeleteEventObserver.off();
|
||||
this.obsDefaultTimeChange && this.obsDefaultTimeChange.off();
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue